I am trying to implement quick sort in java and I have one doubt. So here's my quick sort code:
package com.sorting;
public class QuickSort implements Sort {
#Override
public int [] sort(int[] arr) {
return quickSort(arr, 0, arr.length - 1);
}
private int [] quickSort(int[] numbers, int low, int high) {
if (low < high) {
int q = partitionTheArrayAroundPivot(numbers, low, high);
if (low < q)
quickSort(numbers, low, q);
if ((q+1) < high)
quickSort(numbers, q + 1, high);
}
return numbers;
}
private int partitionTheArrayAroundPivot(int[] numbers, int low, int high) {
int pivot = selectPivot(numbers, low, high);
int i = low;
int j = high;
while (true) {
while (numbers[i] < pivot) {
i++;
}
while (numbers[j] > pivot) {
j--;
}
if ( i <= j) {
swap(numbers, i, j);
i++;
j--;
} else {
return j;
}
}
}
private int selectPivot(int[] numbers, int low, int high) {
return numbers[high];
}
private void swap(int[] numbers, int i, int j) {
int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
}
Doubt 1: We keep increasing the index i till we hit a number which is >= pivot
while (numbers[i] < pivot)
i++;
Similarly we keep decreasing the index j till we hit a number which is <= pivot
while (numbers[j] > pivot)
j--;
So, this means that both indexes will also come out of the loop if both hits pivots at two different places e.g. 1,0,1 here if pivot is 1, then i will be 0 and j will be 2. And the below condition will be satisfied
if (i <= j) {
....
}
but in that case it won't be able to sort the above array (1,0,1) because after swapping we are increasing i and decreasing j so the value become i = j = 1. After that i will hit the third element i.e 1 and will again come out of the loop with value i = 2 and similarly j = 0 and we will not be able to sort the array.
So where's the problem? Am I missing something?
I would rewrite the code a little so that selectPivot returns the index instead:
private int selectPivotIndex(int[] numbers, int low, int high) {
return high;
}
Then the partitioning funcion can move the pivot aside, and sort the remaining items according to pivot value. A single loop will do it, in this implementation duplicate pivots will end up on right side:
private int partitionTheArrayAroundPivot(int[] numbers, int low, int high) {
int pivotIndex = selectPivotIndex(numbers, low, high);
swap(numbers, pivotIndex, high); // Not needed if selectPivotIndex always returns high
int newPivotIndex = low;
for(int i = low; i < high; i++)
{
if(numbers[i] < numbers[pivotIndex])
{
swap(numbers, i, newPivotIndex);
newPivotIndex++;
}
}
swap(numbers, newPivotIndex, pivotIndex);
return newPivotIndex;
}
Finally, a small adjustment needs to be done in the quickSort method so that we don't end up in an eternal loop:
if (low < q)
quickSort(numbers, low, q - 1);
This approach is IMHO easier to understand and debug, hope it works for you.
Use
while (numbers[i] <= pivot) and
while (numbers[j] >= pivot) and your code will work
Related
Trying to do Quick sort.
logic -> maintaining two variables to place pivot element at correct index. Taking 1st element as pivot. int i for RHS of pivot and Int j for LHS, if they cross each other then j is correct index for pivot.
#include<iostream>
using namespace std;
int partition(int arr[], int low, int high){
int pivot = arr[low];
int i = low+1;
int j = high;
while (i<j)
{
while(arr[i]<=pivot) i++;
while(arr[j]> pivot) j--;
if(i<j) {
swap(arr[i], arr[j]);
}
swap(arr[j], arr[low]);
return j;
}
}
void QuickSort(int arr[], int low , int high){
if(low >= high ) return;
if(high>low){
int pivotindx = partition(arr, low , high);
QuickSort(arr,low, pivotindx-1);
QuickSort( arr, pivotindx+1, high);
}
}
void printquicksort(int arr[] , int n){
cout << " Quick SORT IS HERE BROOOO " << endl;
for (int i = 0; i < n; i++)
{
cout << " " << arr[i] << " " ;
}
}
int main()
{
int arr []={3,4,5,1};
int n= sizeof (arr)/ sizeof (arr[0]);
QuickSort(arr,0,n-1);
printquicksort(arr,n);
return 0;
}
Using i and j for LHS and RHS is type of Hoare partition scheme. The code has a potential issue when using low for the pivot, the while(arr[i]<=pivot) i++; may never encounter an element > pivot and scan past the end of the array. For Hoare partition scheme, the pivot and elements equal to the pivot can end up anywhere, and the partition index separate elements <= pivot and elements >= pivot, so the index needs to be included in one of the recursive calls. Example of a post-increment and post-decrement version of Hoare with the partition code included in QuickSort:
void QuickSort(int *a, int lo, int hi)
{
int i, j;
int p, t;
if(lo >= hi)
return;
p = a[lo + (hi-lo)/2];
i = lo;
j = hi;
while (i <= j){
while (a[i] < p)i++;
while (a[j] > p)j--;
if (i > j)
break;
t = a[i]; // swap
a[i] = a[j];
a[j] = t;
i++;
j--;
}
QuickSort(a, lo, j);
QuickSort(a, i, hi);
}
Example of a classic pre-increment and pre-decrement version of Hoare with the partition code included in QuickSort:
void QuickSort(int a[], int lo, int hi)
{
if(lo >= hi)
return;
int p = a[lo+(hi-lo)/2];
int i = lo-1;
int j = hi+1;
int t;
while(1){
while (a[++i] < p);
while (a[--j] > p);
if(i >= j)
break;
t = a[i]; // swap
a[i] = a[j];
a[j] = t;
}
i = j++;
QuickSort(a, lo, i);
QuickSort(a, j, hi);
}
Is it possible to implement QuickSelect algorithm using Hoare partitioning?
At least at first glance it seems that it cannot be done because Hoare partitioning does not return the index of the pivot necessarily.
Am I missing something ?
With Hoare partition scheme, since the pivot or elements equal to the pivot can end up anywhere after a partition step, the base (terminating) case occurs when the partition size is reduced to a single element. Example code. QuickSelectr is the actual function. QuickSelect validates the parameters.
int QuickSelectr(int a[], int lo, int hi, int k )
{
if (lo == hi) // recurse until lo == hi
return a[lo];
int p = a[(lo+hi)/2]; // Hoare partition
int i = lo - 1;
int j = hi + 1;
while (1){
while (a[++i] < p);
while (a[--j] > p);
if (i >= j)
break;
std::swap(a[i], a[j]);
}
if(k <= j)
return QuickSelectr(a, lo, j-0, k); // include a[j]
else
return QuickSelectr(a, j+1, hi, k); // exclude a[j]
}
// parameter check
int QuickSelect(int *a, int lo, int hi, int k)
{
if(a == (int *)0 || k < lo || k > hi || lo > hi)
return 0;
return QuickSelectr(a, lo, hi, k);
}
Using i instead of j for the split:
int QuickSelectr(int a[], int lo, int hi, int k )
{
if (lo == hi) // recurse until lo == hi
return a[lo];
int p = a[(lo+hi+1)/2]; // Carefully note the +1 compared
// to the variant where we use j
int i = lo - 1;
int j = hi + 1;
while (1){
while (a[++i] < p);
while (a[--j] > p);
if (i >= j)
break;
std::swap(a[i], a[j]);
}
if(k < i)
return QuickSelectr(a, lo, i-1, k); // exclude a[i]
else
return QuickSelectr(a, i+0, hi, k); // include a[i]
}
I believe the existing answer presents a sub-optimal solution. You can simply amend Hoare's algorithm to return the index of the pivot, re:
because Hoare partitioning does not return the index of the pivot necessarily.
To do this, you select the first element of your array as the pivot and then you essentially ignore it, partitioning the remaining sub-array arr[1:] as you would normally. Then, at the end, you swap arr[0] with the element of the index you normally return.
This works since (vanilla) Hoare's algorithm returns an index idx such that:
for all j in [lo, idx], arr[j] <= arr[idx]
for all j in [idx, hi], arr[idx] <= arr[j]
Swapping your pivot with the element at arr[j] maintains this invariant.
Here's an example implementation written in Solidity (since I've had to implement such a thing in a smart contract in the past):
function partition
(
uint256[] memory arr,
uint256 lo,
uint256 hi
)
public
pure
returns (uint256)
{
uint pivot = arr[lo];
uint i = lo;
uint j = hi + 1;
while (true) {
do {
i++;
} while (i < arr.length && arr[i] < pivot);
do {
j--;
} while (arr[j] > pivot);
if (i >= j) {
// swap with pivot
(arr[lo], arr[j]) = (arr[j], arr[lo]);
return j;
}
(arr[i], arr[j]) = (arr[j], arr[i]);
}
}
What I am is doing is using a quicksort algorithm, so that my pivot element(which will always be the first element of the array gets positioned to its appropriate position in the sorted array and I am calling this method again until I do not position the element at a given rank. Is there a better solution?
Here is my code:
public static int arbitrary(int a[],int x,int y,int rank)//x and y are first and last indecies of the array
{
int j=y,temp;
if(x<y)
{
for(int i=y;i>x;i--)
{
if(a[i]>a[x])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
j--;
}
}
temp=a[x];
a[x]=a[j];
a[j]=temp;
//System.out.println("j is "+j);
if(j==rank)
return a[j];
else if(rank<j)
return arbitrary(a,x,j-1,rank);
else
return arbitrary(a,j+1,y,rank);
}
else
return 0;
}
The algorithm you have implemented is called Quickselect.
Just select a random pivot and to get rid of the worst case with O(n²) time complexity.
The expected runtime is now about 3.4n + o(n).
Quickselect is probably the best tradeoff between performance and simplicity.
An even more advanced pivot selection strategy results in 1.5n + o(n) expected time
(Floyd-Rivest Algorithm).
Fun Fact: With deterministic algorithms you can't go better than 2n. BFPRT for example needs about 2.95n to select the median.
Best way to find Rank element by using the QuickSort method:
In QuickSort, at every iteration you are able to get fixed one pivot element.
When the RankElement == PivotIndex, and break the condition and return the value.
public class FindRank {
public void find(int[] arr, int low, int high, int k) {
if (low < high) {
int pivot = partition(arr, low, high, k);
find(arr, low, pivot - 1, k);
find(arr, pivot + 1, high, k);
}
}
public int partition(int[] arr, int low, int high, int k) {
int pivotIndex = high;
while (low < high) {
while (low < high && arr[low] <= arr[pivotIndex]) {
low++;
}
while (low > high && arr[high] >= arr[pivotIndex]) {
high--;
}
if (low < high) {
swap(arr, low, high);
}
}
swap(arr, pivotIndex, high);
if (pivotIndex == k) {
System.out.println("Array Value:" + arr[k] + " index:" + k);
return k;
}
return high;
}
private void swap(int[] arr, int low, int high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
}
I'm trying to implement a merge sort algorithm for arraylists of strings, but i can't seem to find the bug which is screwing up the ordering of the arraylist.
private static void sort(java.util.ArrayList<String> a)
{
// End recursion
if (a.size() < 2)
{
return;
}
int mid = a.size() / 2;
java.util.ArrayList<String> left = new java.util.ArrayList<String>();
int i;
for (i = 0; i < mid; i++)
{
left.add(a.remove(i));
}
java.util.ArrayList<String> right = new java.util.ArrayList<String>();
// Copy the second half to the "right"
for ( ; i < a.size(); i++)
{
right.add(a.remove(i));
}
sort(left);
sort(right);
merge(a, left, right);
}
private static void merge(java.util.ArrayList<String> result, java.util.ArrayList<String> left,java.util.ArrayList<String> right)
{
int i, l, r;
i = l = r = 0;
while (l < left.size() && r < right.size())
{
if ((left.get(l)).compareTo(right.get(r)) < 0)
{
result.add(left.get(l));
l++;
}
else
{
result.add(right.get(r));
r++;
}
i++;
}
while (l < left.size())
{
result.add(left.get(l));
l++;
i++;
}
// Append rest of the values in the right half, if any...
while (r < right.size())
{
result.add(right.get(r));
r++;
i++;
}
}
The issue is in the sort function.
What happens when you use a.remove(i) ? Element at index i gets removed from the array, so the element that was previously at index i+1 is now at index i, and the array size is decremented. If you then do i++, and again a.remove(i), you will skip one element in the array.
In your sort function, when calling merge, you should check that a.size() == 0. You will see it is not always the case. The merge function seems fine, but your array splitting is incorrect: you are forgetting that using remove(int i) changes the array; its size and the indexes of its elements.
When given an array of elements, how can I count the amount of element comparisons the algorithm performs?
It's not as easy as just adding a counter to the partition method.
private void partition(int low, int high) {
int i = low, j = high;
// Get the pivot element from the middle of the list
int pivot = numbers[low + (high-low)/2];
// Divide into two lists
while (i <= j) {
if (array[i] < pivot) {
i++;
compareCount++; //it's not as easy as just adding this here
}
else if (numbers[j] > pivot) {
j--;
compareCount++;
}
else (i <= j) {
exchange(i, j);
i++;
j--;
}
}
You can't do this because it counts the comparison just made, and not any of the comparisons made when it evaluates to false.
I've tried changing the if (array[i] < pivot) to while (array[i] < pivot) (for j, too), but I feel like I'm still missing out on something if I do it that way.
Ideally you should be able to do it by analyzing the logic. But if you want to have the program do it for you during its run-time then, easy way to do this is to have a function for performing comparison operation. Let the function increment a global/static variable everytime it is called and then do the comparison. At the end of all your logic just print this global/Static variable.
public class Myclass{
public static int compareCount = 0;
}
/*
* PASS parameter comparisonMethod as following
* 0 for ==, 1 for >, 2 for >=, 3 for < and 4 for <==
* Method returns true or false by doing appropriate comparison based on comparisonMethod
*/
public bool compare(int i, int j, int comparisonMethod)
{
Myclass.compareCount++;
if(comparisionMethod ==0) return i==j?true:false;
if(comparisionMethod ==1) return i>j?true:false;
if(comparisionMethod ==2) return i>=j?true:false;
if(comparisionMethod ==3) return i<j?true:false;
if(comparisionMethod ==4) return i<=j?true:false;
}
private void partition(int low, int high) {
int i = low, j = high;
// Get the pivot element from the middle of the list
int pivot = numbers[low + (high-low)/2];
// Divide into two lists
while (compare(i, j, 4)) {
if (compare(array[i], pivot, 3)) {
i++;
}
else if (compare(numbers[j], pivot, 2)) {
j--;
}
else (compare(i, j, 4)) {
exchange(i, j);
i++;
j--;
}
}
// At the end of the logic, Myclass.compareCount wil give number of comparisons made.
The parition method of quick sort would be called till the size of array is not 1 in which case our array would be sorted.In your code when you have founf the position at which pivot would be swapped (in your else if portion)you are not supposed to incrment the comparecounter.
You can use this modified partition method
partition(A,p,r)
{
pivot=A[r]; // Taking last element as pivot
i=p;
j=r;
while (true)
{
while(A[i] < pivot && i <= r )
{
++comparecounter;
++i;
}
while(A[j] >= pivot && j >= 0)
{
--j;
++comparecount;
}
if(i < j)
{
Exchange A[i] and A[j]
}
else
{
return j;
}
In above algorithm you could make countcompare as global which would would incrment fro each call to partition.This would would count the no of comparisions made.
You can embed the compare count increment inside each if statement...
if ((compareCount++ != -1) && (array[i] < pivot))
...
else if ((compareCount++ != -1) && (numbers[j] > pivot))
...
else if ((compareCount++ != -1) && (i <= j))
It'll always evaluate the first clause of the if, always return true, and always then execute the second.