I am trying to write a simple algorithm for moving the elements around pivot such that the elements on the left of pivot is smaller than the pivot and the element on the right of pivot is greater than it (the same step in quick sort). I have written code that works, but after that I changed the algorithm to the below, and it is not working.
The idea of the algorithm is simple.
Have two pointers, one at the beginning of the array and one at the end of array. If the elements pointed by i are lesser than pivot, keep skipping it until we find a greater element; and keep decrementing j until we find an element greater than the smaller element. [It is a common algorithm]
Now the code
private static void sortByPivot(int[] a)
{
Random r = new Random();
int index = r.nextInt(a.length);
int pivot = a[index];
System.out.println("pivot = " + pivot);
int i =0 , j = a.length -1;
while(true)
{
while(a[i] <= pivot) i++;
while( j >0 && a[j] >= pivot) j--;
if(i <= j)
{
break;
}
else{
swap(a,i,j);
}
}
swap(a,i,index); //swap the pivot and the i
}
Swap routine :
private static void swap(int[] a, int i , int j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
When I run this with the following array
int[] a = {46,3,8,4,2,6,244,76}
and when the pivot is picked as 4
the output is
4 3 8 46 2 6 244 76
For some other pivots that are in the edge, I get a null pointer exception.
Is there any flaw in the implementation. The logic seems right to me. I have been trying it for quite sometime but I am unable to fix it.
Check this implementation. It works exactly on the same principle. Try to compare and see where you are going wrong.
Note that you are supposed to swap the values of a[i] and a[j] if i <= j and also break from the loop. You are doing it on the else, which is wrong, because if a[i] is greater than the pivot and a[j] is less than the pivot by the time you reach the if, then they should be swapped if i <= j.
If you're just trying to sort it all, (I know you said "partition" but would it be a problem if the whole thing was sorted?) there are built in methods for that
java.util.Arrays.sort(int[] a)
java.util.Arrays.sort(int[] a, int fromIndex, int toIndex)
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Arrays.html
If you're requirement is to do it yourself (homework!) then try the debugging approach above; write on paper what you expect, then step through completely to see what happens
The logic is not correct. You have just written the code. Just take a moment and dry run this program on any input and you will directly find the flaw.
A better approach would be to take the approach depicted here on wikipedia
This is the inplace version of partitioning an array used in quick sort. Hope it solves your problem.
For anyone that's coming here years later I think the issue is that this conditional should be >= .
if(i <= j)
{
break;
}
Looks like the code is breaking the loop in the first iteration.
All that's running is the last swap function.
Related
I'm new at algorithms and want to know how to solve this task. A detailed Java solution is desirable.
You have been given an integer n≥2 and an integer array A[0..n−1] which is sorted in
nondecreasing order . All numbers repeat 1 time but one number repeats 2 times, how do you find the number that repeat 2 times. Using data structures and built-in functions was not allowed. Complexity of algorithm should be O(lg(n))
You can use a custom binary search algorithm.
We can observe that if there would be only values that repeated once (which occur twice), then the size of the array is even, and we have for each even index i: A[i] == A[i+1].
Since there is exactly one triplet, the array's size is odd, and so if we find an even index i for which A[i] == A[i+1] we know that the triplet did not occur at the left side of i. If however A[i] != A[i+1], then we are sure the triplet occurred at the left side of i.
This is all we need to perform a binary search. We should just make sure we always make our checks at even indices.
Here is an implementation in JavaScript:
function binarySearchTriplet(arr) {
let lo = 0, hi = arr.length - 1; // even
while (lo < hi) {
// Take mid, but force it to be even
mid = (lo + hi) >> 2 << 1;
if (arr[mid] === arr[mid+1]) {
lo = mid + 2;
} else {
hi = mid;
}
}
return arr[lo];
}
let arr = [1,1,4,4,4,5,5,7,7,2,2,6,6,8,8];
console.log(binarySearchTriplet(arr)); // 4
Suppose you have a method subArrayLeftShift(a,i) which shifts left the sub array a[i,...,n-1] when n is the array length. That means that the elements a[i+1],...,a[n-1] are moving one place to the left, and the original a[i] will become the last one.
More formally, here is the function implementation:
public static void subArrayLeftShift(int[] a, int i){
if (a.length == 0) return;
int last = a.length - 1;
int insertToLast = a[i];
for (; i < last; i++){
a[i] = a[i + 1];
}
a[last] = insertToLast;
}
Now for the question: implement a function that receives an unsorted array, and returns the minimal number of calls to subArrayLeftShift for sorting the array.
In the interview I couldnt find the way to do it. I succeed to find the minimal number of calls for every example I wrote for intuition, but couldn't find a way for generalizing it.
Do you know how to solve it?
I propose the following algorithm to solve the problem:
Find the minimum number in the array that is not sorted ( has a smaller number on the right in the array). Let this number be x.
Count how many numbers in the array are greater than the previously found number x. Let this number be y.
Since for each call to the function, the unsorted number will end up at the last position, the optimum strategy is to call the function for each unsorted number in increasing order. Using what was found previously we start with x. We continue with the next unsorted number bigger than x, because in this way, it will end up on the right of x, hence it will be sorted. Continue in the same fashion. How much? How many bigger number than x we have? Well, that's y. So as a total, the number of calls to the function is 1 + y.
public static int minimumCalls(int[] a) {
int minCalls = 0;
for (int i = 0; i < a.length - 1; i++) {
for (int j = i+1; j < a.length; j++) {
if (a[i] > a[j]) {
minCalls++;
break;
}
}
}
return minCalls;
}
The idea behind my thinking is that you must invoke the method once whenever there exists in the SubArray any value less than the current i. The name of the method subArrayShiftLeft, i feel, is designed to throw you off and drag your attention away from thinking of this easily.
If there's any values less than the current one further on in the array, just invoke the method.
It's much easier to think of this as moving a single larger value to the end of the array than trying to shift the smaller ones to the left.
I'm studying this book since yesterday and after I've understood and applied the first algorithm, I tried to go on my own and look in a different way. Here's, in Java, the shown algorithm :
public static int[] sort(int[] array)
{
for(int i = 1; i < array.length; i++){
int value = array[i];
int j = i - 1;
while(j >= 0 && array[j] > value){
array[j + 1] = array[j];
j--;
}
array[j+1] = value;
}
return array;
}
And here is mine :
public static int[] sortb(int[] array)
{
for(int i = 0; i < array.length; i++){
int value = array[i];
int j = i;
while(j < array.length && value > array[j]){
array[j] = array[j + 1];
j++;
}
array[j] = value;
}
return array;
}
For 1 million of function call for each, I got 32 ms for the first and 25 ms for the second. I'm still beginning with algorithms, so I have no idea of the meaning.
I found why your sort is so much faster than original one: Because you are not doing sort at all.
In your code
int value = array[i];
int j = i;
while(j < array.length && value > array[j]) { ... }
Because j = i, so value == array[j] before you get into the while loop, and thus your while loop body will never execute. Your sort result will be wrong. That's the main reason why your code is extremely faster.
In my experience (read student's experience) this kind of different values have little meaning.
Maybe you had a background process that took / released a bit more of resources from one to another.
Maybe the specific case you tried to arrange was better for one of the algorythms than the other.
Maybe, if you used different random arrays, one of them was closer to be sorted than the other..
To have good measures, you usually have to do a lot of tests, not only one. For example, generate 1k arrays of 10k elements each and sort each of this array with both algorythms..
Anyway, sometimes specific features of a language or a compiler can generate different results for algorythms with theoretically the exact same complexity (one example: once I noticed in C++ if you traverse a 2-dimensional array first by columns and then by rows, you will have a very different speed than if you do it the other way around; but I don't remember which one was faster tbh).
I'm aware that the best and average time is O(n log(n)), and the worst time is O(n^2). Can anyone tell me when these cases actually occur in this particular implementation? How does it vary across other implementations?
private void quickSort(int low, int high) {
int i = low, j = high;
// Get the pivot element from the middle of the list
int pivot = array[low + (high - low) / 2];
// Divide into two lists
while (i <= j) {
// If the current value from the left list is smaller then the pivot
// element then get the next element from the left list
while (array[i] < pivot) {
i++;
}
// If the current value from the right list is larger then the pivot
// element then get the next element from the right list
while (array[j] > pivot) {
j--;
}
// If we have found a values in the left list which is larger then
// the pivot element and if we have found a value in the right list
// which is smaller then the pivot element then we exchange the
// values.
// As we are done we can increase i and j
if (i <= j) {
swap(array, i, j);
i++;
j--;
}
}
// Recursion
if (low < j)
quickSort(low, j);
if (i < high)
quickSort(i, high);
}
Any feedback is highly appreciated.
The worst case happens when you chose a pivot such that, after the "Divide into two lists" part, ends up at one extreme of the array (far left or far right). The reason is: one of the two recursive calls will do almost no work, while the other will do almost all of the work again.
For example: you have a permutation of the numbers from 1 to 100 and you choose a pivot in the middle. Say the pivot is 1. You run the "Divide into two lists" part and now you have to sort 0 elements on the left and 99 elements on the right.
The best case happens when the pivot splits in half the two lists (in the permutation example, when you choose a pivot that is around 50).
I am trying to write quick sort in java. But getting a stack over flow error for very small set of inputs. In the createArray function I am taking input by the Scanner object.
Please somebody help me in this.
public class quickSort {
static int[] ar;
int number;
public static void main(String args[]) {
CreatingArray ca = new CreatingArray();
ar = ca.createArray();
ca.printArray(ar);
int len = ar.length;
sort(0,(len-1));
System.out.println("");
System.out.println("Array after QuickSort:");
ca.printArray(ar);
}
public static void sort(int l,int h) {
int i=l;
int j=h;
int temp =0;
int pivot = ar[(l + (l+h)/2)];
while(i <= j) {
while(ar[i] < pivot) {
i++;
}
while(ar[j] > pivot) {
j--;
}
if (i<=j) {
temp = ar[i];
ar[i] = ar[j];
ar[j] = temp;
i++;
j--;
}
}
if(l<j){
sort(l,j);
}
if(i<h) {
sort(i,h);
}
}
}
int pivot = ar[(l + (l+h)/2)];
This line is wrong. It only gets the center point when l == 0. If, say, l == 4 && h == 7 (e.g. the upper half of an 8-element array), you get 4 + (4+7)/2 which is 9 and thus outside the bounds. You really want l + (h-l+1)/2.
The first thing that can happen because of this is ArrayIndexOutOfBoundsException, but you never get that because you always recurse on the lower partition first and run into the second problem. Swap the two ifs at the end of the function to see this in action.
The second thing that can happen is that, because pivot is not actually an element in the range [i, j], the element search at the start of the loop can go crazy. In particular, pivot could be a very small value that is smaller than any in the range. The i search will terminate immediately (leaving i == l the way it started), while the j search will run way beyond i, which means the if won't be entered either, i still doesn't change, and the main loop terminates. Because i is unchanged, i<h is still true (assuming l<h was), and you enter the recursive call with exactly the same arguments you just had, which means the next call will do exactly the same thing as the current one, ending in infinite recursion.
I think your recursive calls don't end... debug your code with very small input - say 3 numbers... check when and how sort() is being called..