Java sorting with quicksort algorithm - java

I'm trying to code a quicksort program in java.here is my partition function
int partition(int[] array, int start, int end)
{
int last = end - 1;
int first = start;
int pivot = array[start];
while (first < last)
{
while (first < last && pivot <= array[last])
last = last - 1;
array[first] = array[last];
while (first < last && pivot > array[first])
first = first + 1;
array[last] = array[first];
}
array[first] = pivot;
return first;
}
and that is my quicksort function
void quickSort(int array[], int start, int end) {
int index = partition(array, start, end);
if (start < index - 1)
quickSort(array, start, end - 1);
if (index < end)
quickSort(array, index, end);}
But when I test the code in Junit it gives me error. I need to change quickSort or partition function. What can I do with that.

Here:
if (start < index - 1)
quickSort(array, start, end - 1);
Why do you call quickSort for lower partition from start, to end-1? Shouldnt it be:
if (start < index - 1)
quickSort(array, start, index - 1);
And the upper part is from index+1 to end.
Moreover, it is better to check the limits like this before the partitioning and remove your if statements:
if (end <= start) { return; }

To me, it just seems much less intuitive to write the functions like those above. Take this sample code that I just wrote (and tested) as an example, and see if you can understand it and use it to help you through your code. NB: This is slightly different than yours in that it picks a pivot at random.
void quicksort(int[] nums)
{
if (nums.length > 1)
{
quicksort(nums, 0, nums.length);
}
}
//[left,right)
private void quicksort(int[] nums, int left, int right)
{
if (left < right - 1)
{
int position = left;
int pivot = pickPivot(left, right);
if (pivot != right - 1)
swap(nums, pivot, right-1);
for (int i = left; i < right - 1; i++)
{
if (nums[i] <= nums[right-1])
{
swap(nums, position, i);
position++;
}
}
swap(nums, position, right-1);
quicksort(nums, left, position);
quicksort(nums, position + 1, right);
}
}
//[left,right)
private int pickPivot(int left, int right)
{
return rand.nextInt(right-left) + left; // rand is a Random object
}
private void swap(int[] nums, int start, int end)
{
int temp = nums[end];
nums[end] = nums[start];
nums[start] = temp;
}
As others have said, it can be difficult to debug recursive functions. If you can't figure it out from here, I suggest using a debugger. Eclipse's debugger is pretty easy to use and very helpful. This may be frustrating and difficult to figure out, but the process is one that every programmer (should and) has to go through.
Once you think you have it figured out, use a testing function similar to this one to test it out (I would initially start off with a smaller size in case there are errors, this way it will be easier to debug):
void testQuickSort()
{
for(int i = 0; i < 1000; i++)
{
int size = rand.nextInt(100)+1;
int[] nums = new int[size];
for (int j = 0; j < size; j++)
{
nums[j] = rand.nextInt(10);
if (rand.nextBoolean())
{
nums[j] *= -1;
}
}
quicksort(nums);
assert sorted(nums) == true;
}
}
private boolean sorted(int[] array)
{
for (int i = 0; i < array.length-1; i++)
{
if (array[i] > array[i+1])
return false;
}
return true;
}

stack overflow error on line "quickSort(array, index, end);
When array is big enough recusion call too many times the same function overflowing stack.
Instead of recursion place pairs of indexes in a stack and call the quickSort() for top pair.
void quickSort(int array[], int start, int end) {
int index = partition(array, start, end);
if (start < index - 1)
// quickSort(array, start, end - 1);
push(start, end - 1);
if (index < end)
// quickSort(array, index, end);}
push(index, end);
while(stack is not empty) {
//get next pair and call quickSort
}

Related

Randomized QuickSort IndexOutOfBounds exception [duplicate]

This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
Closed 1 year ago.
this is the QuickSort Randomized that I've come up with, but it constantly throws out IndexOutOfBounds exception. Could I have some help with it? Thanks!
import java.util.Random;
public class QuickSort {
void quickSort(int[] A, int start, int end) { // Initially: start = 0, end = n-1
while (start < end) {
int iOfPartition = randomisedPartition(A, start, end);
if (iOfPartition - start < end - iOfPartition) {
quickSort(A, start, iOfPartition - 1);
start = iOfPartition + 1;
} else {
quickSort(A, iOfPartition + 1, end);
end = iOfPartition - 1;
}
}
}
int randomisedPartition(int[] A, int start, int end) {
Random rng = new Random();
int randomNum = rng.nextInt(end + 1 - start) + start;
swap(A, A[randomNum], A[start]);
return hoarePartition(A, start, end);
}
int hoarePartition(int[] A, int start, int end) {
int pivot = A[start];
int i = start;
int j = end;
while (i < j) {
while (A[i] <= pivot && i < end) i++;
while (A[j] > pivot && j > start) j--;
if (i < j) swap(A, A[i], A[j]);
}
swap(A, A[start], A[j]);
return j;
}
void swap(int[] A, int i, int j) {
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}
I keep getting an arrayindexoutofbounds error.
I echo the sentiment of the comment above, you should learn to use the debugger or print statements to try to piece together what is happening.
Still, I couldn't resist investigating.
Check out what you are doing here in the call to swap. You are taking the value which is in the randomNum position with A[randomNum]
swap(A, A[randomNum], A[start]); // incorrectly indexing here
But then inside swap, you are repeating the process, and taking the value at the A[A[randomNum]] which does not necessarily exist.
int temp = A[i]; // indexing here again
So your problem is that you are incorrectly indexing twice. You should use [] only in the swap function, not in the randomisedPartition function. randomisedPartition should send swap indexes, not indexed values.
How did I figure this out ?
I tried a call with very simple data
int data[] = {5,3,4};
new Example().quickSort(data, 0, 2);
and got an index out of bounds 5 error. That's how you debug.

Debugging : Mergesort

Trying to implement merge sort in Java. I've gone over my code a bunch in my head and I feel like it should be working, but obviously I'm doing something wrong. Heres the code
public static void mergeSort(int[] input, int start, int end) {
if (end - start < 2)
return;
int mid = (start + end) / 2;
mergeSort(input, start, mid);
mergeSort(input, mid + 1, end);
merge(input, start, mid, end);
}
public static void merge(int[] input, int start, int mid, int end) {
if (input[mid - 1] <= input[mid])
return;
int i = start;
int j = mid;
int tempIndex = 0;
int[] temp = new int[end - start]; //combined size of both arrays being merged
/*if i is >= mid, then that means the left portion of the array is done being sorted
and vice versa if j >= end. Once this happens, we should be able to
just copy the remaining elements into the temp array
*/
while (i < mid && j < end) {
temp[tempIndex++] = (input[i] <= input[j]) ? input[i++] : input[j++];
}
//Copying left over elements in left portion
while (i < mid)
temp[tempIndex++] = input[i++];
//Copying left over elements in right portion
while (j < end)
temp[tempIndex++] = input[j++];
//Copy the sorted temp array into the original array
//This is where I think I must be messing up
for (int k = 0; k < temp.length; k++) {
input[start + k] = temp[k];
}
}
I think it must be that im not copying correctly the temp array with the sorted elements back into the original array, but I'm not sure. I wrote comments on my code explaining my logic.
Take a look at the following changes:
Calculating mid
int mid = start + (end - start) / 2;
Assigning pointers i,j correctly.
int i = start;
int j = mid+1;
Correct size of temp array.
int [] temp = new int[end-start+1];
Corrected while loops condition in the code.
class Solution{
public static void mergeSort(int[] input, int start, int end)
{
if (end == start ) return;
int mid = start + (end - start) / 2;
mergeSort(input, start, mid);
mergeSort(input, mid+1, end);
merge(input, start, mid, end);
}
public static void merge(int[] input, int start, int mid, int end) {
// No Need of the under mentioned instruction
// if(input[mid-1] <= input[mid]) return;
int i = start;
int j = mid+1;
int tempIndex = 0;
int [] temp = new int[end-start+1]; //combined size of both arrays being merged
/*if i is >= mid, then that means the left portion of the array is done being sorted and vice versa if j >= end. Once this happens, we should be able to just copy the remaining elements into the temp array */
while(i <= mid && j <= end){
temp[tempIndex++] = (input[i] <= input[j]) ? input[i++] : input[j++];
}
//Copying left over elements in left portion
while(i <= mid)
temp[tempIndex++] = input[i++];
//Copying left over elements in right portion
while(j <= end)
temp[tempIndex++] = input[j++];
//Copy the sorted temp array into the original array
//This is where I think I must be messing up
for(int k = 0; k < temp.length; k++){
input[start+k] = temp[k];
}
}
public static void main(String[] args){
int[] input = {9,4,6,8,5,7,0,2};
mergeSort(input,0,7);
for(int i : input)
System.out.println(i);
}
}

Need assistance JAVA quicksort recursion

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.

Java QuickSort Best Case Array Generation

I've been banging my head on the table on this one.
I need to create an n sized array that is optimized for QuickSort Partition. It will be used to demonstrate the growth of QuickSort's best case. I know that for best case, QuickSort must select a pivot that divides the array in half for every recursive call.
I cannot think of a way to create an n-sized optimized array to test. Any help would be greatly appreciated.
Here is the algorithm in Java.
public class QuickSort {
private int length;
private void quickSort(int[] a, int p, int r) {
if (p < r) {
int q = partition(a, p, r);
quickSort(a, p, q - 1);
quickSort(a, q + 1, r);
}
}
private int partition(int[] a, int p, int r) {
int x = a[r];
int i = p - 1;
for (int j = p; j < r; j++) {
if (a[j] <= x) {
i++;
exchange(a, i, j);
}
}
exchange(a, i + 1, r);
return i + 1;
}
public void exchange(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
QuickSort(int[] a) {
if (a == null || a.length == 0) {
return;
}
length = a.length;
quickSort(a, 0, length - 1);
}
}
I know this is an old question, but I had the same question and finally developed a solution. I'm not a Java programmer, so don't blame me for Java code issues, please. I assumed that the quicksort algorithm always takes the first item as a pivot when partitioning.
public class QuickSortBestCase
{
public static void generate(int[] arr, int begin, int end)
{
int count = end - begin;
if(count < 3)
return;
//Find a middle element index
//This will be the pivot element for the part of the array [begin; end)
int middle = begin + (count - 1) / 2;
//Make the left part best-case first: [begin; middle)
generate(arr, begin, middle);
//Swap the pivot and the start element
swap(arr, begin, middle);
//Make the right part best-case, too: (middle; end)
generate(arr, ++middle, end);
}
private static void swap(int[] arr, int i, int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
private static void fillArray(int[] arr)
{
for(int i = 0; i != arr.length; ++i)
arr[i] = i + 1;
}
private static void printArray(int[] arr)
{
for(int item : arr)
System.out.print(item + " ");
}
public static void main(String[] args)
{
if(args.length == 0)
return;
int intCount = Integer.parseInt(args[0]);
int[] arr = new int[intCount];
//We basically do what quicksort does in reverse
//1. Fill the array with sorted values from 1 to arr.length
fillArray(arr);
//2. Recursively generate the best-case array for quicksort
generate(arr, 0, arr.length);
printArray(arr);
}
}
This program produces the same output for the array of 15 items, as described here: An example of Best Case Scenario for Quick Sort. And in case someone needs a solution in C++:
template<typename RandomIterator,
typename Compare = std::less<typename RandomIterator::value_type>>
void generate_quicksort_best_case_sorted(RandomIterator begin, RandomIterator end)
{
auto count = std::distance(begin, end);
if (count < 3)
return;
auto middle_index = (count - 1) / 2;
auto middle = begin + middle_index;
//Make the left part best-case first
generate_quicksort_best_case_sorted(begin, middle);
//Swap the pivot and the start element
std::iter_swap(begin, middle);
//Make the right part best-case, too
generate_quicksort_best_case_sorted(++middle, end);
}
template<typename RandomIterator,
typename Compare = std::less<typename RandomIterator::value_type>>
void generate_quicksort_best_case(RandomIterator begin, RandomIterator end)
{
{
auto current = begin;
RandomIterator::value_type value = 1;
while (current != end)
*current++ = value++;
}
generate_quicksort_best_case_sorted(begin, end);
}

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