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.
Related
I know that addition is commutative therefore I want to perform a storing operation with shorthand operation += at line 46 but the answer differs only when I put the parenthesis, also when when I don't put parenthesis I get wrong answer.
The line is in the merge function.
CODE:
public class ProblemA {
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<Integer> (Arrays.asList(1, 3, 2, 3, 1));
int n = arr.size();
//calling mergesort
int total = countPair(arr, n);
System.out.println(total);
}
public static int countPair(ArrayList<Integer> arr, int n) {
int total = mergeSort(arr, 0, n - 1);
return total;
}
public static int mergeSort(ArrayList<Integer> arr, int low, int high) {
//termination condition
if (low >= high) return 0;
//firstly we'll disassociate the elements of array on elementary level
int mid = (low + high) / 2;
//we'll store our count value inside the counter variable
int inv = mergeSort(arr, low, mid);
inv += mergeSort(arr, mid + 1, high);
inv += merge(arr, low, mid, high);
return inv;
}
public static int merge(ArrayList<Integer> arr, int low, int mid, int high) {
//AIM: make a double loop and traverse through the elements and increase the right side pointer
//whenever condition (arr[i]>2*arr[j] is satisfied)
//int i = 0;
int total = 0;
int j = mid + 1;
for (int i = low; i<= mid; i++) {
//looping till we run out the right hand side or condition is not satisfied
while (j<= high && arr.get(i) > 2 * arr.get(j)) {
j++;
} **
* total += (j - (mid + 1)); ** * //parenthesis error here
}
//Now we can move to merging part
ArrayList<Integer> temp = new ArrayList<Integer> ();
int left = low, right = mid + 1;
while (left<= mid && right<= high) {
if (arr.get(left)<= arr.get(right)) {
temp.add(arr.get(left++));
} else {
temp.add(arr.get(right++));
}
}
//for the last right or left remaining element
while (left<= mid) {
temp.add(arr.get(left++));
}
while (right<= high) {
temp.add(arr.get(right++));
}
//now we can copy the remaining elements from temp list to arr
for (int i = low, k = 0; i<= high; i++) {
arr.set(i, temp.get(k++));
}
return total;
}
}
OUTPUT(with parenthesis) "total += (j-(mid+1))":
2
OUTPUT(without parenthesis) "total += j-mid+1":
16
This is happening because if you use parenthesis, java will decide which expression you want to evaluate first. If you don't use parenthesis then everything goes executed from left to right.
Example:
int total=1;
int total1=1;
int total2=1;
total+=total1-total2+1;
System.out.println(total);
Output : 2
The reason is here, expression got evaluated as total=total+total1-total2+1;
i.e. total=1+1-1+1=2;
For parenthesis based expression, it gets evaluated first & then others:-
int total=1;
int total1=1;
int total2=1;
total+=(total1-(total2+1));
System.out.println(total);
Output : 0 , evaluation will happen like below :-
total=total+(1-(1+1));
total=1+(1-(2));
total=1+(-1);
total=0;
Reference : https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html
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));
}
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);
}
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
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
}