Attempting to implement QuickSort using Hoare Partition Scheme, but I am running into a problem where changing the index of the pivot causes overflow, regardless of array size. Code:
public void quickSort(int[] l, int min, int max){
if (min < max){
int p = partition(l, min, max);
quickSort(l, min, p);
quickSort(l, p+1, max);
}
}
public int partition(int[] l, int min, int max){
int pivot = l[min];
int i = min - 1;
int j = max +1;
while(true){
do{
i++;
}while(l[i] < pivot);
do{
j--;
}while(l[j] > pivot);
if (i >= j) {
return j;
}
//Swap
int temp = l[i];
l[i] = l[j];
l[j] = temp;
}
}
This implementation chooses the low-index (named min here) as the pivot element, and this works just fine. However, changing the pivot element to any other index, causes a StackOverflow Error regardless of the size of the array that is being sorted. (Error refers to line 3, where partition() is called) I would preferably have the pivot element chosen at random within the (min,max) range. What is causing this?
EDIT:
The array used is generated as follows:
public static int[] generateRandomArray(int size, int lower, int upper){
int[] random = new int[size];
for (int i = 0; i < random.length; i++) {
int randInt = ThreadLocalRandom.current().nextInt(lower, upper+1);
random[i] = randInt;
}
return random;
}
In one of the Overflow cases I used this:
genereateRandomArray(10, 0, 9);
For some concrete examples, running the code above but changing the pivot element to say, l[max-1] or l[min+1], l[min+2] etc gives StackOverflow on my end.
The solution to my problem was as user MBo pointed out to swap the pivot element to the first index of the array, as the algorithm itself relies on the pivot being on index 0. This is what I had overlooked.
(int i = min - 1; is correct however, and stays that way.)
We can see that at the first step i becomes equal to min, comparison of pivot element with itself fails and increment does not occur more:
int pivot = l[min];
int i = min - 1;
...
do{
i++;
}while(l[i] < pivot);
Exclude pivot element from comparison (int i = min;) and exchange it with partition one (seems l[j]) at the end
Using the middle value for pivot is working for me. Here is a complete example:
public static void quickSort(int[] l, int min, int max){
if (min < max){
int p = partition(l, min, max);
quickSort(l, min, p);
quickSort(l, p+1, max);
}
}
public static int partition(int[] l, int min, int max){
int pivot = l[(min+max)/2];
int i = min - 1;
int j = max + 1;
while(true){
do{
i++;
}while(l[i] < pivot);
do{
j--;
}while(l[j] > pivot);
if (i >= j) {
return j;
}
int temp = l[i];
l[i] = l[j];
l[j] = temp;
}
}
public static int[] generateRandomArray(int size, int lower, int upper){
int[] random = new int[size];
for (int i = 0; i < random.length; i++) {
int randInt = ThreadLocalRandom.current().nextInt(lower, upper+1);
random[i] = randInt;
}
return random;
}
public static void main(String[] args) {
int[] A = generateRandomArray(10, 0, 9);
long bgn, end;
bgn = System.currentTimeMillis();
quickSort(A, 0, A.length-1);
end = System.currentTimeMillis();
for(int i = 1; i < A.length; i++){
if(A[i-1] > A[i]){
System.out.println("failed");
break;
}
}
System.out.println("milliseconds " + (end-bgn));
}
Related
I implemented a QuickSort Algorithm which only works for 7 elements and then gives a StackOverflow Error for 8 elements or more. Goes into an infinite loop. Works fine for the number of elements that are present in the array but if I add one more element, it returns a StackOverflow Error
Here is my code:
public class QuickSort
{
public void main()
{
QuickSort o = new QuickSort();
int arr[] = {8,5,2,10,1,7,3};
o.sort(arr,0,arr.length-1);
for(int i=0;i<arr.length;i++)
System.out.print(arr[i]+" ");
}
void sort(int []arr,int l,int h)
{
if(l<h)
{
int pi = partition(arr,l,h);
sort(arr,l,pi);
sort(arr,pi+1,h);
}
}
int partition(int arr[],int l,int h)
{
int pivot = arr[l];
int i=l,j=h;
while(i<j)
{
while(arr[i]<=pivot)
{
i++;
}
while(arr[j]>pivot)
{
j--;
}
if(i<j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
int t = arr[j];
arr[j] = pivot;
arr[l] = t;
return j;
}
}
I believe the problem is that you don't put the pivot in the right place.
Here is your code with a small change:
int partition(int arr[],int l,int h){
int pivot = arr[l];
int i= l,j=h;
while(i < j){
while( i < j && arr[i]<=pivot){ i++;}
while( i < j && arr[j]>pivot){ j--;}
if(i< j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//here it is determined where the pivot should go. It is easiest to understand with an example
//after the loop arr can be 3 1 2 4 5
//the pivot being 3 should be switched with the number 2 but index j sometimes points to number 2 and sometimes to number 4
//the following code determines the desired index
int lowerInd = arr[j] <= pivot ? j : j - 1;
int t = arr[lowerInd];
arr[lowerInd] = arr[l];
arr[l] = t;
return lowerInd;
}
Also, in your sort method, call sort(arr,l,pi - 1); instead of sort(arr,l,pi);
void sort(int[] arr,int l,int h){
if(l<h){
int pi = partition(arr,l,h);
sort(arr,l,pi - 1);
sort(arr,pi+1,h);
}
}
I am (for homework, if that influences how the answers are given) supposed to be writing a class for quicksort to sort an array from a txt or dat file provided by the user. I present the user with an menu option in the driver class to select sort type and then pass the array to the quicksort class for sorting, after which it is returned back to the driver class to be written to a file. I believe the return is operating correctly, as I get the unsorted array printed to the file, however it only appears to be making one pass through the quicksort loop (determined by only one printing of the "Last Loop" string to the console.
The addition bits of code are part of the assignment, allowing a user to select Insertion sort to complete the sort if only 50 or 100 items remain in the unsorted array based on user choice. I have tried running this program commenting those two if statements out and still the program does not correctly sort. I am certain the input is read correctly as I have an addition HeapSort class that does sort correctly, and insertion sort returns correctly if that option is selected and the file size is less than 50 or 100 ints. I cannot seem to get the QuickSort of a larger file to trigger though.
class Quicksort{
int partition(int arr[], int lb, int ub){
int i = lb, j = ub;
int temp;
int pivot = arr[0];
while (i <= j) {
while (arr[i] < pivot){
i++;
}while(arr[j] > pivot){
j--;
}if (i <= j){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}return i;
}
int[] quicksort(int arr[], int lb, int ub, boolean insertLarge, boolean insertSmall){
if (insertLarge && arr.length <= 100){
System.out.println("Large");
insertionSort(arr);
}
else if (insertSmall && arr.length <= 50){
System.out.println("Small");
insertionSort(arr);
}
else{
int[] intArrCopy = new int[arr.length-1];
for (int z = 1; z < intArrCopy.length; z++){
intArrCopy[z-1] = arr[z];
}
System.out.println("Last Loop");
int index = partition(arr, lb, ub);
if (lb < index-1){
quicksort(intArrCopy, lb, index-1, insertLarge, insertSmall);
}if (index < ub){
quicksort(intArrCopy, index, ub, insertLarge, insertSmall);
}
}return arr;
}
public static void insertionSort(int x[]){
int h, k, y;
for (k=1; k < x.length; k++){
y = x[k];
for (h = k-1; h>=0 && y < x[h]; h--){
x[h+1] = x[h];
}x[h+1] = y;
}
}
}
And how it is being called from driver class:
private static void processChoice(int choice, int[] intArr){
switch (choice){
case 1:
p = intArr.length;
output = new int[p];
startTime = System.nanoTime();
output = quick.quicksort(intArr, intArr[0], intArr[p-1], insertLarge, insertSmall);
endTime = System.nanoTime();
System.out.println("Time Taken: "+(endTime-startTime)+" nanoseconds.");
break;
}
}
Assuming a hybrid sort, try something like this:
void quicksort(int arr[], int lb, int ub){
if ((ub-lb) < 100){ // < 32 may be fastest
System.out.println("Large");
insertionSort(arr, lb, ub);
return;
}
int index = partition(arr, lb, ub);
if (lb < index-1)
quicksort(arr, lb, index-1);
if (index < ub)
quicksort(arr, index, ub);
}
public static void insertionSort(int x[], int lb, int ub){
int h, k, y;
for (h = lb+1; h <= ub; h++){
y = x[h];
for (k = h; k > lb && x[k-1] > y; k--)
x[k] = x[k-1];
x[k] = y;
}
}
For school I have to write a quicksort for an Array of strings. I think my partition is good but I keep getting error with the recursion.
public static int partition(String[] input, int max) {
// pivot is dividing point
// I keeps track of lesser values
// count is just a counter
// Pivots in place
int pivot = 0;
int i = 1;
int count = 1;
while (count <= max) {
if (input[count].compareTo(input[pivot]) < 0) {
input[i] = input[count];
i = i + 1;
}
if (count == max) {
input[i] = input[pivot];
input[pivot] = input[i];
}
count++;
}
return pivot;
}
public static void qSort(String[] input) {
int index = partition(input, input.length - 1);
int count = 0;
if (count < index - 1) {
partition(input, index - 1);
}
if (index + 1 < count && count < input.length) {
partition(input, input.length - 1);
}
count++;
}
Your implementation has a lot of mistakes. First of all, you're always choosing the pivot to be the element at index 0 of the array. The idea of quicksort is that you choose some pivot, partition the array around it, then recursively apply quicksort on both partitions around the chosen pivot. How do you identify the start and end of the partitions you'll call quicksort recursively on? You need to pass those as arguments to your partition method as others have mentioned in the comments, same thing for the quicksort method itself.
public void swap (String [] array, int i, int j) {
String tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static int partition (String [] array, int low, int high) {
String pivot = array[low];
int index = low+1;
for (int i = low+1; i <= high; i++) {
if (pivot.compareTo(array[i]) > 0) {
swap(array, i, index);
index++;
}
}
swap(array, low, index-1);
return index-1;
}
public static void qsort(String [] array, int low, int high) {
if (low < high) {
int p = partition(array, low, high);
qsort(array, low, p);
qsort(array, p+1, high);
}
}
Obviously this isn't the best way to choose the pivot as some arrays will make the algorithm perform very poorly. A better method would be to use a random pivot each time.
I'm trying to make a code for finding kth smallest element using partitions.
When I run this code, I think it works pretty fine for less than 5 numbers, but whenever I use bigger data--like over 100 or 10000 datas--and try to find kth number, it keeps giving me back java.lang.StackOverflowError message, or just simply complete wrong answer.
Here is my code:
import java.util.Scanner;
public class test2 {
public static int partition(double arr[], int begin, int end){
int pivotIndex = begin;
double pivot = arr[pivotIndex];
begin++;
int p = begin;
int r = (int) end;
while (p <= r){
while (p <= r && arr[p] < pivot)
p++;
while(p<=r && arr[r] > pivot)
r--;
if (p > r){
swap(arr, pivotIndex, r);}
else {swap(arr, p, r);
}
}
return r;}
public static void swap(double[] arr, int a, int b){
if (a <= b){
double temp;
temp = arr[a];
arr[a] = arr[b];
arr[b] = temp; }
}
public static double selection(double[] arr, int begin, int end, int k){
int pivotIndex = begin;
int i = pivotIndex;
pivotIndex = partition(arr, begin, end);
if (i == k-1){
return arr[(k-1)];
}
else if (i > k-1 ){
return selection(arr, begin, i-1, k);
}
else{
return selection(arr, i+1, end, k-i); }}
public static void main (String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Selection Test\n");
int n, i;
System.out.println("Enter number of elements");
n = scan.nextInt();
double arr[] = new double [n];
System.out.println("\nEnter "+ n +" elements");
for (i = 0; i < n; i++)
arr[i] = scan.nextDouble();
System.out.println("\nEnter the kth smallest element you'd like to find");
int k = scan.nextInt();
kthsmall(arr, k);
System.out.println(arr[k-1
]);
}
public static void kthsmall(double[] arr, int k){
selection(arr, 0, arr.length -1, k);}
I would really appreciate if anyone could tell me where my mistakes are.
I'm having a bit of a problem writing a recursive function that sorts an array in java recursively . Right now it appears as though I have an infinite loop, and I can't seem to figure out where.
The primary function "rec_piv" searches from index1 to the pivot point and sorts the first half, then switches from the pivot point to the length of the array and sorts the second half. All of the comparisons are recorded by an int. [The array is of random size from 2 to 2014]
Thanks very much in advance!
public class Recursive_pivot {
private Random random_size = new Random();
private int size = random_size.nextInt(1024) + 2;
public int[] a = new int[size];
private Random elements = new Random();
/* variable for rec_piv */
public int temp=0;
public int temp2=0;
private Random rand_pivot = new Random();
private int pivot = rand_pivot.nextInt(size) + 2;
private int first_half =a[0+1];
private int second_half=a[pivot+1];
private int first_index=0; //first half of the array
private int second_index=pivot; //second half of the array
//The pivot is randomly chosen.
public int comparisons =0; //count the number of comparisons.
public void fill(){
for (int q=0; q<a.length; q++) {
/* filling the array */
a[q] = elements.nextInt(100 ) + 1;
}
}
public void rec_piv(int first_index, int second_index) {
if(first_index < pivot) {
if(first_half > a[first_index]) {
comparisons++;
temp = a[first_index];
a[first_index] = a[first_half];
a[first_half] = temp;
}
rec_piv(first_index+1, second_index);
}
if(second_index < a.length) {
if(second_half > a[second_index]) {
comparisons++;
temp2 = a[second_index];
a[second_index] = a[second_half];
a[second_half] = temp2;
}
rec_piv(first_index, second_index+1);
}
} //end of rec_piv
}//end of class.
You are trying to do a QSort here is a simple version of it.
public void quickSort(int array[], int start, int end)
{
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) // check that there are at least two elements to sort
{
int pivot = array[start];
while (k > i)
{
while (array[i] <= pivot && i <= end && k > i)
i++;
while (array[k] > pivot && k >= start && k >= i)
k--;
if (k > i)
swap(array, i, k);
}
swap(array, start, k);
quickSort(array, start, k - 1); // quicksort the left partition
quickSort(array, k + 1, end); // quicksort the right partition
}
else // if there is only one element in the partition, do not do any sorting
{
return; // the array is sorted, so exit
}
}
public void swap(int array[], int index1, int index2)
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
int temp = array[index1]; // store the first value in a temp
array[index1] = array[index2]; // copy the value of the second into the first
array[index2] = temp; // copy the value of the temp into the second
}
from this site.
http://www.mycstutorials.com/articles/sorting/quicksort