I'm pretty sure I understand how quicksort works, but I can't find the bug that's causing my attempt at implementing it to not work. I've looked through this for hours and can't figure out what's wrong. Please help me! Here is the entire file (It's just quicksort - nothing extra. The array is just random numbers for testing the quicksort.)
public class Quicksort{
public static void main(String args[]){
int[] arr = {5,1,4,3,7,0,9,2,6,8};
quicksort(arr, 0, arr.length-1);
for(int x : arr)
System.out.print(x+" ");
}
public static void quicksort(int[] arr, int start, int end){
if(end-start<2)
return;
int pivot = (end-start)/2;
int i = start;
int k = end;
while(k>i){
while(arr[i]<arr[pivot]&&k>i&&i<=end)
i++;
while(arr[k]>arr[pivot]&&k>=i)
k--;
if(k>i){
swap(arr, i, k);
}
}
swap(arr, pivot, i);
quicksort(arr, 0, i);
quicksort(arr, k, arr.length-1);
}
public static void swap(int[] a, int x, int y){
int temp = a[x];
a[x] = a[y];
a[y] = temp;
}
}
As it is right now, the loop never terminates... it's a forever infinite loop! Please help me figure out what's wrong.
Do yourself a favor and learn how to use a debugger. It makes solving this kind of problems very easy.
Your base case should be if(end-start<1) - You only want to stop sorting when the number of elements is 1 (i.e. if start and end are equal)
Your while loops should just be while(arr[i]<arr[pivot]) and while(arr[k]>arr[pivot])
This
if(k>i){
swap(arr, i, k);
}
should be
if(k>=i){
swap(arr, i, k);
i++;
k--;
}
swap(arr, pivot, i); is unnecessary.
Your recursive call should be quicksort(arr, start, k); and quicksort(arr, i, end);
Couple of things that stand out --
Your end condition seems incorrect. If your array has just 2 elements, it won't sort them.
Also, after you do the swap, you need to increment and i and decrement k.
while(arr[i]<arr[pivot]&&k>i&&i<=end)
Clearly you have had array index problems. All these tests aren't required in a working Quicksort, and the ones that are are in the wrong order.
Related
I'm trying to implement a quickSort algorithm after learning about it.
package qsort;
public class QuickSort {
public static void main(String[] args) {
int arr[] = {10,16,8,12,15,6,3,9,5,100};
quickSort(arr,0,(arr.length-1));
for(int number:arr) {
System.out.println(number);
}
}
public static void quickSort(int[] arr, int l, int h) {
if(l<h) {
int j=partition(arr,l,h); //pivot position as j retrieved as the one sorted element
int[] left = new int[j];
int[] right = new int[(arr.length-j)];
for(int index=0;index<j;index++) {
left[index]=arr[index];
}
for(int index=j;index<arr.length;index++) {
right[index-(j)]=arr[index];
}
quickSort(left,0,j); //Sorts the first half of the array (i.e the elements before pivot
quickSort(right,j+1,arr.length-1); //SOrts the second half after pivot
}
}
public static int partition(int[] arr, int l, int h) {
if(arr[l]>arr[h]) {
swap(arr[l],arr[h]);
}
int pivot = arr[l];
int i=l;
int j=h; //i starts from the first and increments; j starts from last and decrements
while(i<j) {
do {
i++;
}while(arr[i]<=pivot); //i keeps incrementing until i points to a value greater than pivot
do {
j--;
}while(arr[j]>pivot); //j keeps decrementing until it finds a value less than pivot
if(i<j) {
swap(arr[i],arr[j]);
}
}
swap(arr[l],arr[j]); // swapping the first element l with the element in j so that the pivotal element can be ordered
return j; //finally j points to the one sorted index where pivot should be placed
}
public static void swap(int a, int b){
int temp=a;
a=b;
b=temp;
}
}
And I've been clueless about IndexOutOfBoundsException and I'm not able to find out where or how it occurs. Any help would be much appreciated.
There are these issues:
swap doesn't swap. It just swaps the values of two local variables, but has no knowledge about an array. When swap exits, those two local variables are discarded and nothing really changed. You need to pass the array reference to swap and the two indices that are involved, and then swap should change the values in that array at those two given indices.
left gets j values, but then a recursive call is made with h equal to j, which is an index that is out of range. The left array has j elements, so its last index is j-1, not j.
right gets the remaining values, but then the recursive call is made with l equal to j+1, but that has no relation to right, whose relevant values start at index 0, not at index j+1.
The whole idea to copy values into new arrays left and right is wrong. This is not how quicksort is supposed to work. Even if left and right would be successfully sorted, this does not have any impact on arr, which remains unsorted. So you'd have done work for nothing. Quicksort is an inplace sorting algorithm, so you should always be working with arr, not with copies of partitions of it.
Here is the correction of the relevant functions:
public static void quickSort(int[] arr, int l, int h) {
if(l<h) {
int j=partition(arr,l,h);
// Don't create new arrays here, but sort the partition in-place:
quickSort(arr,l,j-1);
quickSort(arr,j+1,h);
}
}
public static int partition(int[] arr, int l, int h) {
if (arr[l]>arr[h]) {
swap(arr, l, h); // swap needs the array reference, and two indices
}
int pivot = arr[l];
int i=l;
int j=h;
while(i<j) {
do {
i++;
}while(arr[i]<=pivot);
do {
j--;
}while(arr[j]>pivot);
if(i<j) {
swap(arr, i, j); // see above
}
}
swap(arr, l, j); // see above
return j;
}
// swap needs the array reference and the indices to perform the swap in the array
public static void swap(int[] arr, int i, int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
I've been clueless about IndexOutOfBoundsException and I'm not able to find out where or how it occurs.
Debugging means you read the stack trace that includes this error message. It will give you the line number where it occurs (it was on the first line in partition). Then when you have identified that line, you can start to really use a debugger, setting breakpoints and inspecting variables. One of the first things you would notice, is that arr never changes: nothing gets moved in it. And so you would continue to debug and resolve one thing after the other. It is what I did with your code.
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!
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;
}
I have to implement a multi threaded Merge Sort and Quick sort in Java for my algorithms class and compare them to my single threaded versions. However, I have never multithreaded before.
Is the code I have able to be multi threaded or do I have to start again?
Here is my code for the single thread algorithms
Merge Sort. the sort() method is part of the strategy pattern I have to implement.
#Override
public int[] sort(int[] list) {
int array_size = list.length;
list = msort(list, 0, array_size-1);
return list;
}
int[] msort(int numbers[], int left, int right) {
int mid;
if (left<right) {
mid = (right + left) / 2;
msort(numbers, left, mid);
msort(numbers, mid+1, right);
merge(numbers, left, mid, mid+1, right);
}
return numbers;
}
void merge(int numbers[], int startA, int endA, int startB, int endB) {
int finalStart = startA;
int finalEnd = endB;
int indexC = 0;
int[] listC = new int[numbers.length];
while(startA <= endA && startB <= endB){
if(numbers[startA] < numbers[startB]){
listC[indexC] = numbers[startA];
startA = startA+1;
}
else{
listC[indexC] = numbers[startB];
startB = startB +1;
}
indexC++;
}
if(startA <= endA){
for(int i = startA; i < endA; i++){
listC[indexC]= numbers[i];
indexC++;
}
}
indexC = 0;
for(int i = finalStart; i <= finalEnd; i++){
numbers[i]=listC[indexC];
indexC++;
}
}
Here is my quick sort
#Override
public int[] sort(int[] list) {
int[] array = quickSort(list, 0, list.length-1);
return array;
}
int partition(int arr[], int left, int right)
{
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
return i;
}
int[] quickSort(int arr[], int left, int right) {
int index = partition(arr, left, right);
if (left < index - 1)
quickSort(arr, left, index - 1);
if (index < right)
quickSort(arr, index, right);
return arr;
}
Cheers!
Short answer - yes, these algorithms can be converted to be multi-threaded without starting from scratch (so far as I can see).
The key elements that make these "easy" to parallelize are:
There are two recursive calls within each implementation
Those two recursive calls operate on separate pieces of data - they shouldn't conflict with each other (e.g. even when working inside the same array, they're operating on different indices)
The method making those recursive calls cannot proceed until both are complete
That it doesn't matter which order those two calls are made in
That's answered some of your questions, hopefully.
Some more advice, not sure how useful this will be:
If you put both recursive calls into a new thread, then the current thread will be idle while waiting for them both to complete
When the number of elements left to work on is small, the overheads of threading may be higher than the gains.
You might want to throttle the number of threads being used for this task in general - you might want to use some form of thread pool or work queue, with a fixed number of threads.
A major advice in this case (a mistake that I've made when I was in your shoes and I've seen many others do it) is to not let the number of threads grow unchecked. Remember that if you start one thread per recursion branch, the main thread will spawn one child thread (assuming one recursive call is done on the main itself), the child thread will spawn an additional thread and so on until you will choke the system if your data set is large.
A more clear alternative would be to start one thread per recursive call, such that each thread spawns two child threads and then joins them.
Either way, make sure to set a limit on the recursion depth that spawns threads (let's say equal to the number of CPU cores) and if the limit is exceeded, call the sort method sequentially on the rest of the levels.