I am trying to implement the quicksort algorithm in Java. The following code is identicall to the one provided by my university professor. It works just fine, until I set as an input the Object[] {1,2,3,14,24,5,454,1,24,2,11} (obviously left = 0 and right = 10). The algorithm seems to fall into an infinite loop, but I cannot find where or why. I have been looking at it for a few days now and i cannot find the bug. I studied other similar problems with the quicksort algorithms on the site, but they did not help in fixing my problem. I would apreciate any ideas.
static int partition (Object[] a, int left, int right, Comparator c){
Object pivot = a[left];
int i = left+1;
int j = right;
while (i<=j){
while(i<=j && c.less(a[i],pivot)){i++;}
while(c.less(pivot, a[j])){j--;}
if (i<j){
Object temp = a[i];
a[i]=a[j];
a[j]=temp;
i++;
j--;
}
}
a[left] = a[j];
a[j] = pivot;
return j;
}
static void quickSort(Object[] a, int left, int right){
if(left<right){
int p = partition(a,left,right, new Comparator());
quickSort(a,left,p-1);
quickSort(a,p+1,right);
}
}
Just to mark it as accepted answer, as markspace pointed out in the comments, the infinite loop occures when i == j. The two while loops fail as well as the if statement so they are left unchanged. Changing the main loop to while (i < j) fixxed the issue. Thank you, markspace, for your help!
Related
In theory it should be possible to write every recursive method into an iterative one.
I know it's pretty easy when the recursive function is tail-recursive. For example, calculating a factorial can be done like this recursive:
// Recursive:
long fact(long n){
return fact(n, 1);
}
long fact(long n, long r){
return n==1 ?
r
:
fact(n-1, r*n);
}
Try it online.
or like this iterative:
// Iterative:
int fact(int n){
int r = 1;
for(; n>1; n--)
r *= n;
return r;
}
Try it online.
This is pretty easy, because we don't use the result of the recursive method for something else, and we only have a single recursive call, where we decrease n by 1 ever iteration.
I also know you should keep a stack for most recursive to iterative conversions. For example, doing a quick-sort can be done like this recursive:
// Recursive:
void quicksort(int[] array, int left, int right){
if(left >= right) return;
int index = partition(array, left, right);
quicksort(array, left, index-1);
quicksort(array, index+1, right);
}
Try it online.
or like this iterative:
// Iterative:
void quicksort(int[] array, int left, int right){
int[] stack = new int[1024]; // Example size, alternative an actual java.util.Stack could be used
int i=0;
stack[i++] = left;
stack[i++] = right;
while(i>0){
right = stack[--i];
left = stack[--i];
if(left >= right) continue;
int index = partition(array, left, right);
stack[i++] = left;
stack[i++] = index-1;
stack[i++] = index+1;
stack[i++] = right;
}
}
Try it online.
But I now want to convert the following recursive method into its iterative form as well:
// Recursive:
int f(int n){
return n<1 ?
0
:
n%2+1 + 3*f(n/2);
}
Try it online.
In this case it uses the recursive result, and multiply that result by 3. I'm not entirely sure how to make this iterative as well. I tried something like this:
// Iterative:
int f(int n){
int[] stack = new int[1024]; // Example size, alternative an actual java.util.Stack could be used
int i=0;
stack[i++] = n;
while(i > 0){
n = stack[--i];
if(n < 1)
stack[i++] = 0;
stack[i++] = n%2+1 + 3*stack[--i];
}
return stack[0];
}
Which obviously it not going to work, since i=1 before it enters the while, then it becomes 0 at n = stack[--i], then 1 again at stack[i++] = n%2+1 + ..., and then 0 again at 3*stack[--i], so it stops after the first iteration, and simply returns stack[0]. How should I convert this recursive method above to an iterative one when I use the result of the recursive-call with other calculations (multiplying by 3 in this case)?
As for the reason why: I want to port this recursive method above to a stack-based language which doesn't have any functions and therefore I'll need the iterative approach (with stack).
Ok, I've been able to figure it out. I first need to fill the entire stack with the n%2+1 parts, dividing 2 every iteration. And as soon as n < 1, I need an inner loop to calculate the result.
Here is what I ended up with:
// Recursive:
int f(int n){ // i.e. n=2
int[] stack = new int[1024]; // Example size, alternative an actual java.util.Stack could be used
int i=0;
while(i >= 0){
stack[i++] = n%2+1;
n/=2;
if(n < 1){
while(i > 0){
n = 3*n + stack[--i];
}
return n;
}
}
return -1; // It should never come here, but the method need a return-statement
}
Try it online.
I ended up with something quiet similar (saw your self-answer only after)
int iterf(int n){
//can also be implemented with an array. easier with list.
List<Integer> sum = new ArrayList<>();
while (true) {
if( n < 1 ) { //equivalent to if(n<1) return 0;
sum.add(0);
break;
}
//for each n, fill list with the fixed argument part: (n%2)+1
sum.add((n%2)+1);
n=n/2;
}
//add to each list element 3 time the next element
//equivalent to 3*f(n/2);
for(int i = sum.size()-2; i >=0; i--) {
sum.set(i, sum.get(i) + (3*(sum.get(i+1))));
}
return sum.get(0);
}
So I think my partition method works but I cannot understand or figure out how to do the kthSmallest method. I no longer get out of bounds errors with my partition method which leads me to think that it works and with testing it seems to work. However, my kthSmallest method often gets stuck in an infinite loop and when it does return a value, it is never the correct value.
I have seen examples online that place the pivot between the two subarrays however for our assignment the pivot is always at the end so I often get confused looking at these examples.
Here is what I have:
class Median{
static int kthSmallest(int[] arr, int left, int right, int k){
int divider = partition(arr, left, right);
if(divider == k-1){
return arr[right];
}
else if(divider > k-1){
return kthSmallest(arr, left, divider-1, k);
}
else{
return kthSmallest(arr, divider, right, (k - divider-1));
}
}
static int partition(int[] arr, int left, int right){
int pivot = arr[right];
int index = left;
for(int i = left; i < right-1; i++){
if(arr[i] < pivot){
swap(arr, index, i);
index++;
}
}
printArr(arr);
System.out.println("divider: " + index);
return index;
}
static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void printArr(int[] arr){
System.out.println();
for(int index = 0; index < arr.length - 1; index++){
System.out.print(arr[index] + ", ");
}
System.out.print(arr[arr.length-1]);
System.out.println();
}
public static void main(String[] args){
int[] arr = {34,-1,0,5,3};
printArr(arr);
//System.out.println(partition(arr, 0, arr.length-1));
//printArr(arr);
System.out.println(kthSmallest(arr, 0, arr.length - 1, 2));
}
}
It seems like what you are trying to do is implement the QuickSelect algorithm, so I recommend taking a look at the Wikipedia article.
Looking at your code, I think you have misinterpreted the "pivot at the end" bit. I believe that what your requirements want is to select the pivot from the end, and then place it in between the two sublists so that your list looks right. For example,
34 -1 0 5 3
should become
-1 0 3 34 5
not
-1 0 34 5 3, where your pivot is not doing its job correctly.
There are also a few problems with your kthSmallest method. Double check the values you pass along in your recursion and you also are missing a case which might cause infinite recursion. Spoilers for if you are absolutely stuck:
You since you don't re-index the list, you should change '(k - divider -1)' to just k.
If left == right then you get unnecessary recursion, so you should just return in that case.
In your partition method, make sure you iterate far enough in the part of the list you are partitioning. Again, just in case you're really stumped:
for(int i = left; i < right-1; i++) should become for(int i = left; i <= right-1; i++)
For refreshing some Java I tried to implement a quicksort (inplace) algorithm that can sort integer arrays. Following is the code I've got so far. You can call it by sort(a,0,a.length-1).
This code obviously fails (gets into an infinite loop) if both 'pointers' i,j point each to an array entry that have the same values as the pivot. The pivot element v is always the right most of the current partition (the one with the greatest index).
But I just cannot figure out how to avoid that, does anyone see a solution?
static void sort(int a[], int left, int right) {
if (right > left){
int i=left, j=right-1, tmp;
int v = a[right]; //pivot
int counter = 0;
do {
while(a[i]<v)i++;
while(j>0 && a[j]>v)j--;
if( i < j){
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
} while(i < j);
tmp = a[right];
a[right] = a[i];
a[i] = tmp;
sort(a,left,i-1);
sort(a,i+1,right);
}
}
When preforming a Quicksort I strongly suggest making a separate method for partitioning to make the code easier to follow (I'll show an example below). On top of this a good way of avoiding worst case run time is shuffling the array you're sorting prior to preforming the quick sort. Also I used the first index as the partitioning item instead of the last.
For example:
public static void sort (int[] a)
{
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
}
private static void sort(int[] a, int lo, int hi)
{
if (hi <= lo) return;
int j = partition(a, lo, hi) // the addition of a partitioning method
sort(a, lo, j-1);
sort(a, j+1, hi);
}
private static int partition(int[] a, int lo, int hi)
{
int i = lo, j = hi + 1, tmp = 0;
int v = a[lo];
while (true)
{
while (a[i++] < v) if (i == hi) break;
while (v < a[j--]) if (j == lo) break;
if (i >= j) break;
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
tmp = a[lo];
a[lo] = a[j];
a[j] = temp;
return j;
}
On top of this if you want a really good example on how Quicksort works (as a refresher) see here.
This should work (will check for correctness in a bit, it works!):
EDIT: I previously made a mistake in error checking. I forgot to add 2 more conditions, here is the amended code.
public static void main (String[] args) throws java.lang.Exception
{
int b[] = {10, 9, 8, 7, 7, 7, 7, 3, 2, 1};
sort(b,0,b.length-1);
System.out.println(Arrays.toString(b));
}
static void sort(int a[], int left, int right) {
if (right > left){
int i=left, j=right, tmp;
//we want j to be right, not right-1 since that leaves out a number during recursion
int v = a[right]; //pivot
do {
while(a[i]<v)
i++;
while(a[j]>v)
//no need to check for 0, the right condition for recursion is the 2 if statements below.
j--;
if( i <= j){ //your code was i<j
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
//we need to +/- both i,j, else it will stick at 0 or be same number
}
} while(i <= j); //your code was i<j, hence infinite loop on 0 case
//you had a swap here, I don't think it's needed.
//this is the 2 conditions we need to avoid infinite loops
// check if left < j, if it isn't, it's already sorted. Done
if(left < j) sort(a,left,j);
//check if i is less than right, if it isn't it's already sorted. Done
// here i is now the 'middle index', the slice for divide and conquer.
if(i < right) sort(a,i,right);
}
}
This Code in the IDEOne online compiler
Basically we make sure that we also swap the value if the value of i/j is the same as the pivot, and break out of the recursion.
Also there was a check in the pseudocode for the length, as if we have an array of just 1 item it's already sorted (we forgot the base case), I thought we needed that but since you pass in the indexes and the entire array, not the subarray, we just increment i and j so the algorithm won't stick at 0 (they're done sorting) but still keep sorting an array of 1. :)
Also, we had to add 2 conditions to check if the array is already sorted for the recursive calls. without it, we'll end up sorting an already sorted array forever, hence another infinite loop. see how I added checks for if left less than j and if i less than right. Also, at that point of passing in i and j, i is effectively the middle index we split for divide and conquer, and j would be the value right before the middle value.
The pseudocode for it is taken from RosettaCode:
function quicksort(array)
if length(array) > 1
pivot := select any element of array
left := first index of array
right := last index of array
while left ≤ right
while array[left] < pivot
left := left + 1
while array[right] > pivot
right := right - 1
if left ≤ right
swap array[left] with array[right]
left := left + 1
right := right - 1
quicksort(array from first index to right)
quicksort(array from left to last index)
Reference: This SO question
Also read this for a quick refresher, it's implemented differently with an oridnary while loop
This was fun :)
Heres some simple code I wrote that doesn't initialize to many pointers and gets the job done in a simple manner.
public int[] quickSort(int[] x ){
quickSortWorker(x,0,x.length-1);
return x;
}
private int[] quickSortWorker(int[] x, int lb, int ub){
if (lb>=ub) return x;
int pivotIndex = lb;
for (int i = lb+1 ; i<=ub; i++){
if (x[i]<=x[pivotIndex]){
swap(x,pivotIndex,i);
swap(x,i,pivotIndex+1);
pivotIndex++;
}
}
quickSortWorker(x,lb,pivotIndex-1);
quickSortWorker(x,pivotIndex+1,ub);
return x;
}
private void swap(int[] x,int a, int b){
int tmp = x[a];
x[a]=x[b];
x[b]=tmp;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I implemented Quick Sort by choosing the first element as pivot. it works fine for general test cases but consider a case when the array is reverse sorted for instance 5 4 3 2 1. I understand where it is throwing a runtime error. but I cant fix it correctly. Is the implementation for first element as pivot correct? Please suggest modifications.
public static void quicksort(int low,int high)
{
if(low<high)
{
int temp=0;
int pivot=a[low];
int large_index=low+1;
int small_index=high;
while(large_index<=small_index)
{
while(a[small_index]>pivot)
small_index--;
while(a[large_index]<pivot)
large_index++;
if(large_index<=small_index)
{
temp = a[large_index];
a[large_index]= a[small_index];
a[small_index]= temp;
large_index++;
small_index--;
}
}
temp = a[small_index];
a[small_index]= a[low];
a[low]= temp;
quicksort(low,small_index-1);
quicksort(small_index+1,high);
}
}
There were multiple flaws:
a) Unnecessary swapping outside the loop
temp = a[small_index]; a[small_index]= a[low]; a[low]= temp;
b) The improper initialization of large_index.
c) Calling the recursive function without checking the values of
small_index and large_index.
I have rectified them,
now the function will look like:
if (low < high) {
int temp = 0;
int pivot = a[low];
int large_index = low;
int small_index = high;
while (large_index <= small_index) {
while (a[small_index] > pivot)
small_index--;
while (a[large_index] < pivot)
large_index++;
if (large_index <= small_index) {
temp = a[large_index];
a[large_index] = a[small_index];
a[small_index] = temp;
large_index++;
small_index--;
}
}
if(low < small_index)
{
quicksort(low, small_index);
}
if(large_index < high)
{
quicksort(large_index, high);
}
}
Now, watching the variables in your piece of code: (your code will fail in the final iteration of any given input unless the input is not sorted)
Consider the input 2,1
1st iteration:
pivot = 2
large_index = 1;
small_index = 1;
while1:
1<=1 -> true
while2: 1>2 false.
while3: 1<2 true. -> large_index++
2nd time in while loop large_index =2 which is > the size of a.
Resulting in IndexArrayOutOfBounds.
Which shows that your initialization of large_index was wrong.
It should have been: large_index = low; instead of low+1;
Hope this helps.
The following loop continues on above high, when large_index already starts above the pivot and it happens that the pivot is larger than every element located after it:
while(a[large_index]<pivot)
large_index++;
There might be other bugs, of course.
After some attempts I was able to make the Quicksort function and it works fine now. #BatScream I have still used the small_index=low+1 in my code. So that was not a mistake I guess. And the original Quick Sort algorithm follows this mechanism. For reference please watch the lecture on QuickSort by Princeton Professor Robert Sedgewick.Please remind if there is a flaw to the edited Quick Sort Algorithm.
public static void quicksort(int low,int high)
{
if(low<high)
{
int temp=0;
int pivot=a[low];
int large_index=low+1;
int small_index=high;
while(large_index<small_index)
{
while(a[large_index]<pivot) {
if(large_index==high)
break;
large_index++;
}
while(a[small_index]>pivot)
{
if(small_index==low)
break;
small_index--;
}
if(large_index<small_index) {
temp = a[large_index];
a[large_index]= a[small_index];
a[small_index]= temp;
large_index++;
small_index--;
}
}
if(a[small_index]<pivot) {
temp = a[small_index];
a[small_index]= a[low];
a[low]= temp;
}
if(low<small_index) //Recursively sort the left half.
{
quicksort(low,small_index-1);
}
if(high>small_index) //Recursively sort the right half.
{
quicksort(small_index+1,high);
}
}
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I'm trying to implement several different types of quicksort in java, but none of my implementations seem to be working. I've looked all over the internet and my code looks very similar to all the code that i've found, so I have no idea whats wrong. My code is as follows: (Note that this isn't complete but I figure if I find whats wrong with one quicksort, the other versions will work as well) Edit: The problem i'm having is that the array doesn't sort correctly. I run a simple method called isSorted to tell me whether the array was sorted correctly. It works with other sorting methods (insertion sort, heap sort etc.) but reports false when used with my implementation of quicksort
public static void quickSort(int[] A) {
flag=0;
quickSortHelper(A, 0, A.length-1);
}
public static void quickSortHelper(int [] A, int low, int high){
if(low<high){
if(flag==0){
int q=DPartition(A, low,high,A[high]);
quickSortHelper(A,low,q-1);
quickSortHelper(A,q+1,high);
}
public static int DPartition(int [] A, int low, int high,int pivot){
int p=pivot;
int i=low;
int j=high-1;
while (i<=j){
while(i<=j && A[i]<=p){
i=i+1;
}
while(j>=i && A[j]>=p){
j=j-1;
}
if (i<j){
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}
int temp = A[i];
A[i] = A[p];
A[p] = temp;
return i;
}
The bug is in your DPartition method. In quick sort, you move in a particular direction, and as soon as swapping is performed, you change the direction you move. But, in the above code, you are moving in both the direction without swapping.
The first inner-while loop finds the location of swap, but instead of swapping, you started with the next inner-while, which starts moving in opposite direction. That is faulty.
You should maintain one more variable to store the direction in the array.
You can try modifying your code like this (Not Tested): -
// No need to pass `pivot` as parameter. Just use `high`.
public static int DPartition(int [] A, int low, int high) {
int i=low;
int j=high;
boolean leftToRight = false;
boolean rightToLeft = true;
while (i <= j) { // Iterate till middle
if (leftToRight) { // Move left to right
while(i <= j && A[i] <= A[j]){
i=i+1; // Move right until condition is satisfied
}
if (i < j) { // If `i` has not moved beyond `j`. Perform Swap
swap(i, j, A); // Pass index for swapping along with array.
}
leftToRight = false; // Toggle to change direction.
rightToLeft = true;
} else if (rightToLeft) { // Move right to left.
while(j >= i && A[j] >= A[i]){
j=j-1;
}
if (j > i) { // If j has not moved beyond `i`. Perform Swap
swap(i, j, A);
}
rightToLeft = false; // Toggle to change the direction
leftToRight = true;
}
}
return i; // Return `index` to split.
}
public static void swap(int p, int q, int[] a) {
System.out.println("p = " + p + ", q = " + q);
int temp = a[p];
a[p] = a[q];
a[q] = temp;
}