Quicksort not completely sorting provided array - java

My Quicksort seems to stop before completely sorting the array, and I've stared myself blind on the code.
I wrote the algorithm according to the related chapters in Java Software Structures - Designing and Using Data Structures (3rd Edition)
Quick Sort:
private static <T extends Comparable<T>> void quickSort(T[] array,int min, int max){
int pIndex;
if (max-min > 0) {
pIndex = partition(array, min, max);
quickSort(array, min, pIndex-1);
quickSort(array, pIndex+1, max);
}
}
Partition:
private static <T extends Comparable<T>> int partition(T[] array, int min, int max) {
int left, right;
T pivot, temp;
int middle = (min+max)/2;
left = min;
right = max;
pivot = array[middle];
while (left < right) {
while (array[left].compareTo(pivot) <= 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
if (left<right) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
temp = array[min];
array[min] = array[right];
array[right] = temp;
return right;
}
The input:
An int[10] array containing the values 0 through 9, shuffled.
The quicksort-function is thus called like: quicksort(nums, 0, nums.length-1)
The output (example):
0
2
1
3
7
4
5
6
8
9
As you can see, the end product seems to be somewhat on the way to a good end-product, but it's stopping prematurely somewhere.
Update:
None of the answers provided so far (the deleted ones included) worked. If nobody is able to spot the bug, would anyone kindly redirect me to a good source for generic algorithms in Java?
I even shamefully attempted to do a pure copypaste of the Quicksort algorithm from the book mentioned above, and while it compiled and ran, it resulted in the same, "almost-correct" output as above. I then questioned whether or not it may be my input data, but nope. It is simply an Integer-array of integers, no duplicates. It's a valid candidate to my understanding.

I was able to get quick sort to sort some test arrays with the following partition function.
private static <T extends Comparable<T>> int partition(T[] array, int min, int max) {
int left, right;
T pivot, temp;
int middle = (min+max)/2;
left = min;
right = max ;
pivot = array[middle];
while (left < right) {
while (array[left].compareTo(pivot) < 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
if (left<right) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
return right;
}
All I changed was the first compareTo comparison to be less than instead of less than or equal to. This allows the pivot to move in the array. This however does mean that the array CANNOT contain duplicates. I also removed the last swap as I couldn't tell what it was doing.
The problems stem from how you deal with the pivot. It doesn't actually partition the array properly.
This also works and allows duplicates.
private static <T extends Comparable<T>> int partition(T[] array, int min, int max) {
int left, right;
T pivot, temp;
int middle = (min+max)/2;
left = min + 1;
right = max ;
pivot = array[middle];
// move partition element to min index
temp = array[min];
array[min] = array[middle];
array[middle] = temp;
while (left < right) {
while (array[left].compareTo(pivot) <= 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
if (left<right) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
// move partition element to partition index
temp = array[min];
array[min] = array[right];
array[right] = temp;
return right;
}
I looked up a copy of the book. The comment tells you what the last swap was trying to do. Which makes my fix of adding a swap at the begging to move the partition element to the min index the correct fix.

while (array[left].compareTo(pivot) <= 0 && left < right)
left++;
while (array[right].compareTo(pivot) > 0)
right--;
These loops are usually written:
while (array[left++].compareTo(pivot) <= 0 && left < right)
;
while (array[right--].compareTo(pivot) > 0)
;
The idea is to stop at the first element that doesn't belong in this partition.

Related

How to count number of comparisons in a quicksort algorithm?

I am trying to count the number of comparisons made with a quicksort algorithm. To my understanding, it should take about 1000 comparisons to sort an array with 80 elements. Right now with what I have, it is spitting out 80 elements takes about 250 comparisons. This doesn't seem right. I'm assuming my counts are in the wrong place, but I really don't know. I know that some of the code isn't here, but it would be way to long if I added everything. Any help would be appreciated.
Here is my code:
public void quickSort(int[] data) {
quickSort(data, 0, data.length - 1);
}
private void quickSort(int[] data, int min, int max) {
if (min < max) {
// create partitions
int indexofpartition = partition(data, min, max);
// sort the left partition (lower values)
quickSort(data, min, indexofpartition - 1);
// sort the right partition (higher values)
quickSort(data, indexofpartition + 1, max);
}
}
private int partition(int[] data, int min, int max) {
int partitionelement;
int left, right;
int middle = (min + max) / 2;
// use the middle data value as the partition element
partitionelement = data[middle];
// move it out of the way for now
swap(data, middle, min);
left = min;
right = max;
while (left < right) {
// search for an element that is > the partition element
while (left < right && data[left] <= partitionelement)
left++;
count++;
// search for an element that is < the partition element
while (data[right] > partitionelement)
right--;
count++;
// swap the elements
if (left < right)
swap(data, left, right);
}
// move the partition element into place
swap(data, min, right);
return right;
}
Need to add braces to your inner while loop. Currently only left++ and right-- is getting executed inside the loop. Updated code would be like:
// search for an element that is > the partition element
while (left < right && data[left] <= partitionelement) {
left++;
count++;
}
// search for an element that is < the partition element
while (data[right] > partitionelement && right >= min) {
right--;
count++;
}
Additional hint: Add a check for the index in the second while loop. Else, it can throw an exception if there isn't any value lesser than the partitionelement.

My quicksort implementation is using way too many comparisons but cannot determine why

I am trying to implement quicksort in java and I am struggling to implement it in an efficient manner. I believe the problem is with my recursive calls but I cannot determine how to fix it. I am using compares to see how many times comparisons are made in hopes of determining where the problem is. The only thing I can think of is requiring a conditional around my recursive statements because the amount of compares needed is the same whether the inputted array is already sorted or seemingly randomized.
public int quickSort(int[] arr, int left, int right) {
//left is lowest index
//right is highest index
int compares = 0;
//calls insertion sort once subsets get smaller than 7 elements
if (right - left < 6) {
compares += insertSort(arr, left, right);
return compares;
}
//calculate random pivot
int pivotIndex = randomInt(left, right);
int pivotValue = arr[pivotIndex];
//moves pivot value to rightmost index
int temp;
temp = arr[pivotIndex];
arr[pivotIndex] = arr[right];
arr[right] = temp;
int pivotFinal = left;
//determines how many values are lower than the pivot, swapping
//smaller values with larger values along the way
for (int i = left; i < right; i++) {
compares++;
if (arr[i] <= pivotValue) {
temp = arr[i];
arr[i] = arr[pivotFinal];
arr[pivotFinal] = temp;
pivotFinal++;
}
}
//moves pivot to final position so that quicksort is complete
temp = arr[pivotFinal];
arr[pivotFinal] = arr[right];
arr[right] = temp;
compares += quickSort(arr, left, pivotIndex - 1);
compares += quickSort(arr, pivotIndex + 1, right);
return compares;
}
public void main() {
QuickSort qs = new QuickSort();
int n = 60;
int[] array = qs.GenerateRandomSequence(n);
int compares = qs.quickSort(array);
}
With an n of 60, one of the sequences required more than 4 million compares, which is much, much worse than the actual worst case runtime.
You have a couple of bugs with your indexes. Your recursion needs to be using your final pivot position.
compares += quickSort(arr, left, pivotFinal - 1);
compares += quickSort(arr, pivotFinal + 1, right);
And you're treating your "right" index differently in different spots. Probably easiest to just use "<=" in your loop
for (int i = left; i < right; i++) // assumes right is after the last index
arr[pivotIndex] = arr[right]; // assumes right IS the last index

kthSmallest method when an array/sub-array of integers is partitioned two parts with pivot at the end

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++)

Understanding why Java selection rank returns max() as final result

I am working out a solution to the following question:
Describe an algorithm to find the smallest one million numbers in one
billion numbers. Assume that the computer memory can hold all one
billion numbers.
The book gives the a selection rank solution but I am having a hard time understanding a few parts of it:
public static int partition(int[] array, int left, int right, int pivot) {
while (true) {
while (left <= right && array[left] <= pivot) {
left++;
}
while (left <= right && array[right] > pivot) {
right--;
}
if (left > right) {
return left - 1;
}
swap(array, left, right);
}
}
public static int rank(int[] array, int left, int right, int rank) {
int pivot = array[randomIntInRange(left, right)];
int leftEnd = partition(array, left, right, pivot); // returns end of left partition
int leftSize = leftEnd - left + 1;
if (leftSize == rank + 1) {
return max(array, left, leftEnd);
} else if (rank < leftSize) {
return rank(array, left, leftEnd, rank);
} else {
return rank(array, leftEnd + 1, right, rank - leftSize);
}
}
I understand most of it, but I do no understand the following two lines above:
if (leftSize == rank + 1) {
return max(array, left, leftEnd);
1. Why are we returning the max of the three variables?
2. Shouldn't we just be returning array[left:leftEnd] or something of that nature?
Congratulations on trying to learn something by carefully studying a book. It's a key skill that seems to be getting rarer.
It makes general sense if the definition of the return value of rank is "there exist exactly a million numbers less than or equal to rank." The definition of max would be something like:
int t = array[left];
for (int i = left + 1; i <= leftEnd; i++)
t = Math.max(t, array[i]);
return t;
Returning the max value is beyond the problem statement and kind of weird. It would be better and simpler just to partition the elements so that the max million are at the top: array[0] through array[999999]. Then find the max only if that's actually needed
Note that because rank is tail recursive, there's a simple iterative version of the same code that I think would be clearer.
I'm also not convinced this code is correct. leftSize == rank in the check makes more sense than leftSize == rank + 1. But without more definitions and calling code, it's hard to say for sure.
The same rank function is used in Cracking the Coding Interview 6th Edition p. 569 (aside from the line: if (leftSize == rank + 1) { is modified to rank - 1).
The max function is provided, listed below:
/* Get largest element in array between left and right indices */
int max(int[] array, int left, int right)
{
int max = Integer.MIN_VALUE;
for(int i = left; i <= right; i++)
{
max = Math.max(array[i], max);
}
return max;
}
As for an explanation: rank(array, rank) returns the element which would be at the ith position of the sorted array.
leftEnd is the position of the pivot. If there are rank - 1 elements before the pivot element, then there are rank elements including said pivot.
I believe that the pivot will always be the max element, therefore the call to max(array, left, leftEnd); can be replaced with return pivot;

Quicksort sorts all but a few items

I have spent a lot of time attempting to figure out what I don't have right; debugging and all but I just cannot seem to put my finger on why my quicksort misses a few items of sorting.
I've copied my code for sort and partition below. I have a feeling it's something extremely obvious I am over looking but I have spent endless hours debugging, researching, and rewriting my code and it always turns out the same.
// quicksort the subarray from a[lo] to a[hi]
private void sort(Comparable[] a, int lo, int hi) {
if((hi-lo)>1){
int pivot = partition(a,lo,hi);
sort(a,lo,pivot);
sort(a,pivot+1,hi);
}
}
// partition the subarray a[lo .. hi] by returning an index j
// so that a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
private int partition(Comparable[] a, int lo, int hi) {
//find middle
int pivotIndex = (hi+lo)/2;
//pick pivot
Comparable pivot = a[pivotIndex];
//create left and right pointers
int left = lo, right = hi;
//start comparing
//compare until left passes right
while(left<right){
//while left is less than pivot move on
while(a[left].compareTo(pivot) < 0) left++;
//while right is greater than pivot move on
while(a[right].compareTo(pivot) > 0) right--;
//if the pointers have passed each other we're done
//if a[left] is greater than a[right] swap them
if(a[left].compareTo(a[right]) > 0){
Comparable holder = a[left];
a[left] = a[right];
a[right] = holder;
//increment/decrement
left++; right--;
}
}
return right;
}
Just remove the left++; right--; and everything should be fine.

Categories

Resources