Incorrect implementation of QuickSort - java

I am learning about Quick Sort from Youtube and am trying to implement the implementation as viewed where the pivot will be swapped with 1 element before the left marker
This is the pseudocode of the QuickSort algorithm
Method
Divide-and-conquer
Pick an element (pivot) from the list
Pivot is arbitrarily chosen
Normally, the first element is selected
Partition the list into two halves such that:
All the elements in the first half is smaller than the pivot
All the elements in the second half is greater than the pivot
After the rearrangement, the pivot element (pivot) occupies a proper position in a sorting of the list.
Recursively
Quick-sort the 1st half
Quick-sort the 2nd half
Java Code
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
public class QuickSort
{
public static void main(String args[])
{
Vector<Integer> container = new Vector<Integer>();
String userinput = "data1.txt";
Scanner myScanner = new Scanner("foo"); // variable used to read file
try
{
//open filename
File inputfile = new File("C:\\Users\\8382c\\workspace\\AdvanceAlgorithmA3_Quicksort\\src\\" + userinput);
myScanner = new Scanner(inputfile);
}
catch(FileNotFoundException e)
{
System.out.println("File cant be found");
}
String line = myScanner.nextLine(); //read 1st line which contains the number of numbers to be sorted
while(myScanner.hasNext())
{
container.add(myScanner.nextInt());
}
System.out.println(line);
/*container.add(7);
container.add(2);
container.add(3);
container.add(4);
container.add(8);
container.add(6);
container.add(8);
container.add(9);*/
quickSort(container,0,7);
for (int i =0;i<container.size();i++)
{
System.out.println(container.get(i));
}
//http://www.algolist.net/Algorithms/Sorting/Quicksort
}
public static int partition(Vector<Integer> container, int left, int right)
{
int i = left, j = right;
int tmp;
int pivot = container.get(left);
i++;
while (i <= j)
{
while ( container.get(i) < pivot)
i++;
while ( container.get(j) > pivot)
j--;
if (i <= j)
{
tmp = container.get(i);
container.set(i, container.get(j));
container.set(j, tmp);
i++;
j--;
}
};
tmp = container.get(left);
container.set(left, container.get(i-1));
container.set(i-1, tmp);
return i-1;
}
public static void quickSort(Vector<Integer> container, int left, int right)
{
int index = partition(container, left, right);
if (left < index - 1)
quickSort(container, left, index - 1);
if (index+1 < right)
quickSort(container, index+1, right);
}
}
The algorithm works for the following numbers : {7,23,4,8,6,8,9}
However it does not work when i try to sort a text file which contains 10000 numbers
What am i doing wrongly in the algorithm ???

UPDATED
For starters, this statement:
quickSort(container,0,7);
Should read:
quickSort(container,0,container.size()-1);
I'm not sure if that was the problem or not. Now let's clean up your code.
Your core function:
public static void quickSort(Vector<Integer> container, int left, int right)
{
int index = partition(container, left, right);
if (left < index - 1)
quickSort(container, left, index - 1);
if (index+1 < right)
quickSort(container, index+1, right);
}
Appears to have an off by one error with regards to plus/minus 1 with the index. This looks more appropriate:
public static void quickSort(Vector<Integer> container, int left, int right)
{
if (left < right)
{
int index = partition(container, left, right);
quickSort(container, left, index);
quickSort(container, index+1, right);
}
}
And your partition function needs some cleanup as well. I had messed this up the first time I posted it. Now that I tested it, I know it to work.
public static int partition(Vector<Integer> container, int left, int right)
{
int i = left-1;
int j = right+1;
int pivot = container.get(left);
while (true)
{
do
{
i++;
} while (container.get(i) < pivot);
do
{
j--;
} while (container.get(j) > pivot);
if (i < j)
{
int tmp = container.get(i);
container.set(i, container.get(j));
container.set(j, tmp);
}
else
{
break;
}
};
return j;
}

Selbie answer does work as expected however it was not the implementation I was looking. I was looking for the version of Quick Sort where the pivot swaps with one before the left marker resulting in one element being in place after every iteration.
After much trial & error , I realised my original algorithm works , I have no idea why it did not worked initially when i first posted the algorithm in the question
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
public class QuickSort
{
public static void main(String args[])
{
Vector<Integer> container = new Vector<Integer>();
container.add(7);
container.add(2);
container.add(3);
container.add(4); // 7 2 3 4 8 6 8 9
container.add(8); // 7 2 3 4 6 8 8 9
container.add(6); // 6 2 3 4 7 8 8 9
container.add(8);
container.add(9);
quickSort(container,0,container.size()-1);
for (int i =0;i<container.size();i++)
{
System.out.println(container.get(i));
}
}
public static int partition(Vector<Integer> container, int left, int right)
{
int i = left, j = right;
int tmp;
int pivot = container.get(left);
i++;
while (i <= j)
{
while ( container.get(i) < pivot)
i++;
while ( container.get(j) > pivot)
j--;
if (i <= j)
{
tmp = container.get(i);
container.set(i, container.get(j));
container.set(j, tmp);
i++;
j--;
}
};
tmp = container.get(left);
container.set(left, container.get(i-1));
container.set(i-1, tmp);
return i-1;
}
public static void quickSort(Vector<Integer> container, int left, int right)
{
int index = partition(container, left, right);
if (left < index - 1)
quickSort(container, left, index - 1);
if (index+1 < right)
quickSort(container, index+1, right);
}
}

Related

Quick sort partition tweak

but I am trying to figure out how I can make so I may choose which ever pivot point I wish, Say for example on this list of integers, 8, 7, 1, 9 , 11, 5 , 6, I wished to choose say key 6 as the pivot point in my code. Or if I wanted to choose 9 or whatever. How could I write this into my code? Any help is much appreciated.
package quicksort;
public class quicky {
private static void 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);
}
private static int partition (int[] arr, int left, int right) {
int pivot = arr[(left + right) / 2];
while(left<= right) {
while(arr[left] < pivot) left++;
while(arr[right]> pivot) right--;
if(left<= right) {
int tmp = arr[left];
arr[left] =arr[right];
arr[right] = tmp;
left++;
right--;
}
}
return left;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array = new int [] { 8, 7, 1, 9, 11, 5, 6};
quicksort(array, 0 , array.length-1);
for(int i = 0; i <array.length; i++)
System.out.print(array[i]+ " ");
}
}
}
Here in your code
private static int partition (int[] arr, int left, int right) {
/*
Here in below code only you need to make your changes and that needs to be through out the same as you are calling this from quicksort also you need to make use of the left and right for that, as everytime you will be passing that otherwise it will be static value.
like you can do
int pivot = arr[left] // for left most as pivot, if you want the last one then have arr[right] as pivot, right now you have mid element as pivot.
*/
int pivot = arr[(left + right) / 2];
while(left<= right) {
while(arr[left] < pivot) left++;
while(arr[right]> pivot) right--;
if(left<= right) {
int tmp = arr[left];
arr[left] =arr[right];
arr[right] = tmp;
left++;
right--;
}
}
return left;
}
Simple method:
Check if value exists in array.
If it does, swap value with value in default pivot index.
Proceed using same code/ lomuto partition.

Cannot call the sort method in my quicksort implementation

I'm trying to code the quick sort algorithm using java. My problem is, that i cannot call the sort method. This is my code:
public class quickSort
{
int partition(int a[], int left, int right)
{
int i= left, j= right, temp;
int pivot=a[j];
//System.out.println(pivot+"pivot");
while(i<=j)
{
while(a[i]<pivot)
i++;
while(a[j-1]>pivot)
j++;
if(i<j)
{
temp=a[i];
a[i]=a[j-1];
a[j-1]=temp;
System.out.println(a[i] +"i");
System.out.println(a[j-1] +"j");
i++;
j--;
}
}
System.out.println(i);
System.out.println(j);
return i;
}
int[] sort(int[] numbers, int left, int right)
{
int x = partition(numbers, left, right);
System.out.println(x +"Qi");
if(left < right)
sort(numbers, left, x-1);
sort(numbers, x+1, right);
return numbers;
}
public static void main(String[] args)
{
quickSort q= new quickSort();
int[] numbers = {2,6,4,9,7,0,1,3,5};
int left = 0, right=numbers.length-1;
q.sort(numbers, left, right);
}
}
problem:
sort(numbers, left, x-1);
sort(numbers, x+1, right);
this recursion is not getting executed
It also results in an out of bounds exception, when i try to code in the partition method.
This is a screenshot of the output I m using it just to display the output and show the IDE I used to execute :
https://drive.google.com/file/d/1j6lHuEONZkO_Dr3ZszoPKh4bNXZgkgbT/view?usp=sharing.
The current code produces in the partition-method either an endless loop or an ArrayIndexOutOfBoundsException depending on your choosen numbers-array. A possible fix is e.g.:
private int partition(int a[], int left, int right) {
int i = left, j = right, temp;
int pivot = a[j];
while (i <= j) {
if (a[i] > pivot) {
temp = a[i];
for (int k = i; k < right; k++) { // remove a[i] and move all elements...
a[k] = a[k + 1]; // ...following a[i] to the left
}
j--; // decrement pivot's index
a[right] = temp; // move a[i] to the right end
} else {
i++;
}
}
return j; // return pivot's modified index
}
Furthermore, in the sort-method you have to ensure for both recursive sort-calls that the left boundary is smaller than the right boundary, e.g.:
private int[] sort(int[] numbers, int left, int right) {
int x = partition(numbers, left, right);
if (left < right) { // left boundary smaller than right one
sort(numbers, left, x - 1);
sort(numbers, x + 1, right);
}
return numbers;
}

QuickSort giving incorrect sorting order

I am new to Java and I am trying to implement QuickSort.
Here is my script below.
public class QuickSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] ={5,6,7,4,1,3};
QuickSort qs = new QuickSort();
qs.quickSort(a,0,a.length-1);
for(int i=0;i<a.length;i++) {
System.out.println(a[i]);
}
}
public void quickSort(int[] a,int left, int length) {
if(left >= length) return;
int index = partition(a,left,length);
if(left < index) {
quickSort(a,left,index-1);
}
else {
quickSort(a,index,length);
}
}
private int partition(int[] a,int l, int length) {
// TODO Auto-generated method stub
int left = l;
int right = length;
int pivot = a[(left+right)/2];
while(left <= right) {
while(left < length && a[left] < pivot) {
left++;
}
while(right >= 0 && a[right] > pivot) {
right--;
}
if(left <= right) {
int temp = a[left];
a[left]=a[right];
a[right]=temp;
left++;
right--;
}
}
return left;
}
}
When , I print the solution I get the following order-
[1,3,6,4,5,7]
I am unable to figure out the error, can anyone please help me fix this problem.
just change this
if(left < index) {
quickSort(a,left,index-1);
}
else {
quickSort(a,index,length);
}
to this
quickSort(a,left,index-1);
quickSort(a,index+1,length);
Since you need to sort array recursively on every partition of the array!
Quicksort breaks the array into two smaller arrays, on either side of the pivot. This means that each call to quicksort should result in two more calls to quicksort. Your code currently calls quicksort recursively, but only on one half.
Quicksort(array)
pick a pivot
Arrays left, right
For each value in array
If value < pivot
Append to left array
Else
Append to right array
Quicksort(left)
Quicksort(right)
Return join(left, right)
Try the following code:
import java.util.ArrayList;
public class MyQuickSort {
/**
* #param args
*/
public static void main(String[] args) {
//int[] a = { 1, 23, 45, 2, 8, 134, 9, 4, 2000 };
int a[]={23,44,1,2009,2,88,123,7,999,1040,88};
quickSort(a, 0, a.length - 1);
System.out.println(a);
ArrayList al = new ArrayList();
}
public static void quickSort(int[] a, int p, int r)
{
if(p<r)
{
int q=partition(a,p,r);
quickSort(a,p,q);
quickSort(a,q+1,r);
}
}
private static int partition(int[] a, int p, int r) {
int x = a[p];
int i = p-1 ;
int j = r+1 ;
while (true) {
i++;
while ( i< r && a[i] < x)
i++;
j--;
while (j>p && a[j] > x)
j--;
if (i < j)
swap(a, i, j);
else
return j;
}
}
private static void swap(int[] a, int i, int j) {
// TODO Auto-generated method stub
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
Taken from here
Below are the edited code you can replace it,
public void quickSort(int[] a,int left, int length) {
if(left >= length) return;
int index = partition(a,left,length);
if (left < index)
quickSort(a, left, index); // left subarray
if (length > index + 1)
quickSort(a, index + 1, length);
}
private int partition(int[] arr,int l, int length) {
// TODO Auto-generated method stub
int pivot = arr[(l + length)/2];
int left = l - 1; // index going left to right
int right = length + 1; // index going right to left
while (true) {
do {
left++;
} while (arr[left] < pivot);
do {
right--;
} while (arr[right] > pivot);
if (left < right){
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
else
return right; // index of last element in the left subarray
}
}
Quicksort is a divide and conquer algorithm. It first divides a large list into two smaller sub-lists and then recursively sort the two sub-lists. If we want to sort an array without any extra space, quicksort is a good option. On average, time complexity is O(n log(n)).
The basic step of sorting an array are as follows:
Select a pivot, normally the middle one
From both ends, swap elements and make all elements on the left less than the pivot and all elements on the right greater than the pivot
Recursively sort left part and right part
Arrays.sort() method in Java use quicksort to sort array of primitives e.g. array of integers or float and uses Mergesort to sot objects e.g. array of String.

Quicksort partitioning

I have the following array:
int[] arr = { 19, 4, 2, 3, 9, 2, 10, 2, 7, 12, 5, 16, 8, 3, 11, 14, 0, 5 };
And now I use quicksort's partitioning to partition the array with pivot element 7:
public static void partition(int[] arr, int low, int high) {
int pivot = arr[low + (high - low) / 2];
int i = low;
int j = high;
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 (arr[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 (arr[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(arr, i, j);
i++;
j--;
}
}
}
I am confused with the outcome:
5 4 2 3 0 2 3 2 5 12 7 16 8 10 11 14 9 19
I thought that every element <= pivot (7) must be to the left and every element > than pivot element must be on the right. But why is 12 left to 7 ?
This implementation cannot guarantee what you would expect. All it does is the following (provided that you change to arr[i] <= pivot, as Achintya Jha suggested, otherwise it can't even guarantee that):
For every pair of values a, b with a <= pivot < b, it is guaranteed that a will be left of b in the end. However, you don't guarantee anything about the exact position of pivot in the final array (only that it's left of all values that are larger).
A partition function in C++ looks like this:
while (first!=last) {
while (pred(*first)) {
++first;
if (first==last) return first;
}
do {
--last;
if (first==last) return first;
} while (!pred(*last));
swap (*first,*last);
++first;
}
return first;
First and last are iterators that point to elements in the array, analogous to your i and j variables. pred is short for predicate, which could be i <= 7 for example. Essentially this function returns the midpoint, so in C++ code, you would get all elements to the left of the midpoint by iterating up to it, and all elements to the right by iterating from it to the end. To make it less confusing:
i should be the first element, j should be the last element.
// get midpoint first
...
// Note. <= is the opposite of >
// Which logically is the same as
// pred is the opposite of !pred
while (i != j) {
while (i <= midpoint) {
++i;
if (i == j) return i;
}
do {
--j;
if (i == j) return i;
} while (i > midpoint);
swap (i, j);
++i;
}
return i;
...
for (int i = 0; i < get_midpoint(...); i++)
for (int i = get_midpoint; i < end_of_array; i++)
Create a class for the quicksort method
package com.Ramesh;
public class QuickSort {
public void sort(int[] a,int left,int right){
if(left<right)
{
int partition=getPartition(a, left, right);
sort(a,left,partition-1);
sort(a,partition+1,right);
}
}
public int getPartition(int[] a,int l,int r)
{
int pivot=a[l];
int left=l;
int right=r;
while(left<right)
{
while(a[left]<pivot){
left++;
}
while(a[right]>pivot){
right--;
}
if(left<right){
int temp=a[left];
a[left]=a[right];
a[right]=temp;
}
}
return right;
}
}
2.Create another class to invoke the sort method
import java.util.Scanner;
public class Execute {
private int[] a;
private int len;
public int[] getA() {
return a;
}
public void setA(int[] a) {
this.a = a;
}
public int getLen() {
return len;
}
public void setLen(int len) {
this.len = len;
}
public static void main(String[] args) {
Execute ex=new Execute();
ex.takeInput();
QuickSort q=new QuickSort();
q.sort(ex.getA(),0,ex.getLen()-1);
System.out.println("Printing the the Sorted Object");
System.out.println(ex);
}
public void takeInput()
{
Scanner s1=new Scanner(System.in);
System.out.println("Please enter the no of element to be sorted");
len=s1.nextInt();
a=new int[len];
System.out.println("Pls enter the elements");
for(int i=0;i<len;i++){
a[i]=s1.nextInt();
}
}
#Override
public String toString(){
StringBuffer s=new StringBuffer("");
for(int i=0;i<this.len;i++){
s.append(this.a[i]+"\n");
}
return s.toString();
}
}

Implementing Quicksort

i am trying to implement quicksort but i am not getting correct results. Here is my code:
public static void quickSort(Comparable[] a, int start, int stop) {
if (start < stop) {
int pivot = partition(a, start ,stop);
System.out.print("Pivot: "+a[pivot]+" Array: ");
printArray(a);
quickSort(a,start,pivot-1);
quickSort(a,pivot+1, stop);
}
}
public static int partition(Comparable[] a, int start, int stop) {
Comparable pivot = a[stop];
int i = start;
int j = stop-1;
while (i < j) {
while( (isLess(a[i], pivot)|| isEqual(a[i], pivot)))
i++;
while((isGreater(a[j], pivot)|| isEqual(a[j], pivot)))
j--;
if(i < j)
swap(a, i,j);
}
swap(a,i, stop);
return i;
}
For input: {51,17,82,10,97,6,23,45,6,73}, i am getting result: 6 6 10 17 23 45 51 73 97 82
For input: {12,9,4,99,120,1,3,10}, i am getting an index out of bounds error. Would appreciate some help in where i am going wrong.
Your two problems are unrelated.
The problem with {51,17,82,10,97,6,23,45,6,73} is — what happens when stop == start + 1? Then i == start == stop - 1 == j, so you never enter the while-loop, so you unconditionally swap(a, i, stop) — even if a[i] was already less than a[stop].
The problem with {12,9,4,99,120,1,3,10} is seemingly that you didn't read the stacktrace. ;-) Assuming you have a decent Java compiler and JVM, it should have given you the exact line-number and problematic index, so you would have seen that the problem is in this line:
while((isGreater(a[j], pivot)|| isEqual(a[j], pivot)))
once j gets to -1. (This will happen if pivot is the very least value in the range of interest.) You just need to add a check for that:
while(j > start && (isGreater(a[j], pivot)|| isEqual(a[j], pivot)))
(and, for that matter, for the corresponding case of i:
while(i < stop && (isLess(a[i], pivot)|| isEqual(a[i], pivot)))
)
. . . and you need to learn how to debug your code. :-)
I recommend you Algorithms: Design and Analysis, very good internet course from Stanford. After this course you will write such codes more easily. It is a bit enhanced version, pivot is chosen as a median of three. Note that you don't have to write your own printArray() function. In Java you can do it with System.out.println(Arrays.toString(numbers)). Also you can observe how to call quickSort() in more elegant way, with only one argument, using method overloading.
public class QuickSort
{
public static void main(String[] args)
{
int numbers[] =
{ 51, 17, 82, 10, 97, 6, 23, 45, 6, 73 };
quickSort(numbers);
System.out.println(Arrays.toString(numbers));
}
public static void quickSort(int[] array)
{
quickSort(array, 0, array.length - 1);
}
private static void quickSort(int[] array, int left, int right)
{
if (left >= right)
{
return;
}
int pivot = choosePivot(array, left, right);
pivot = partition(array, pivot, left, right);
quickSort(array, left, pivot - 1);
quickSort(array, pivot + 1, right);
}
private static int partition(int[] array, int pivot, int left, int right)
{
swap(array, pivot, left);
pivot = left;
int i = left + 1;
for (int j = left + 1; j <= right; j++)
{
if (array[j] < array[pivot])
{
swap(array, j, i);
i++;
}
}
swap(array, pivot, i - 1);
return i - 1;
}
private static void swap(int[] array, int j, int i)
{
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
private static int choosePivot(int[] array, int left, int right)
{
return medianOfThree(array, left, (left + right) / 2, right);
// return right;
}
private static int medianOfThree(int[] array, int aIndex, int bIndex, int cIndex)
{
int a = array[aIndex];
int b = array[bIndex];
int c = array[cIndex];
int largeIndex, smallIndex;
if (a > b)
{
largeIndex = aIndex;
smallIndex = bIndex;
}
else
{
largeIndex = bIndex;
smallIndex = aIndex;
}
if (c > array[largeIndex])
{
return largeIndex;
}
else
{
if (c < array[smallIndex])
{
return smallIndex;
}
else
{
return cIndex;
}
}
}
}

Categories