Reputation: 113
int partition(int A[], int low, int high){
int mid = (low+high)/2;
int pivot = A[mid];
while(low <= high) {
while(low <= high && A[high] >= pivot) {
high--;
}
while (low <= high && A[low] <= pivot) {
low ++;
}
if(low <= high) {
int tmp = A[low];
A[low] = A[high];
A[high] = tmp;
high--;
low++;
}
}
return mid;
}
void quickSort(int A[], int low, int high) {
if (low >= high) {
return;
}
int ppos = partition(A, low, high);//does the swapping and returns the pivot
quickSort(A, low, ppos-1);
quickSort(A, ppos+1, high);
}
This is my quicksort function with a partition to do the swapping and return the pivot so that quicksort recalls itself with a different midpoint. I'm not too familiar with quicksort but this is what i came up with.
The problem is that it compiles fine but when it runs it always crashes. Is there a logical flaw with my functions? Any advice on how to fix my functions so that it'll do quicksort on a random array?
-edit- fixed the crashing error, but partition won't do the sorting properly, any suggestions to change the function so that it does quicksort on the array?
Upvotes: 1
Views: 583
Reputation: 66254
Your partition function is incorrect. There are two primary methods for partitioning a sequence during quicksort: the squeeze and the sweep. The former of these is the one you're attempting and has a high likelihood of fewer swaps than the latter method at the price of a more complicated algorithm.
The Sweep
I'll show you what the simpler sweep method looks like first, as it is honestly the simplest to understand. In general, the algorithm does the following:
This algorithm is actually easier to understand with code:
size_t partition(int A[], size_t len)
{
if (len < 2) // 1.
return 0;
std::iter_swap(A+len/2, A+len-1); // 2. 3.
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i) // 4.
{
if (A[i] < A[len-1])
std::iter_swap(A + pvt++, A+i);
}
std::iter_swap(A + pvt, A+len-1); // 5.
return pvt;
}
Note it is entirely conceivable values larger than the pivot value may well be swapped multiple times as the lower partition is filled during the march. Eventually it all settles out, but these additional swaps are ideally avoided. That is the purpose of the squeeze method, shown next.
The Squeeze
While the sweep has its advantages (most notable being simplicity), minimizing swaps is not among them. Ideally you only perform a swap when you have determined two values are out of place on opposite sides of the eventual landing home of the pivot value. To do this requires you perform a low-to-high sweep simultaneously with a high-to-low sweep, and as soon as each finds an element in the incorrect location, swap those. Eventually the low index and high index meet, and upon doing so you've found the pivot final resting place.
size_t partition(int A[], size_t len)
{
if (len < 2)
return 0;
std::iter_swap(A + len/2, A+len-1);
size_t low = 0, high = len;
while (1)
{
while (low < high && (A[low] < A[len-1]))
++low;
if (low == high--)
break;
while (low < high && !(A[high] < A[len-1]))
--high;
if (low == high)
break;
std::iter_swap(A+low++, A+high);
}
std::iter_swap(A+low, A+len-1);
return low;
}
There a several things going on here that may seem odd. Note the boolean logic for the second inner-while loop that reduces high
. I could have written (A[high] >= A[len-1])
but I wanted to drive home a common mistake. It is critical the condition which reduces high
be logically inverse to that which promotes low
. If low
is promoted because its element is strictly less than the pivot value as we have it here, then high
can only be reduced if its element is not (strictly less than the pivot value). The is no doubt we have it right when shown as coded above, and I simply cannot do justice to the number of times that specific requirement is glossed over and resulted in mysteriously broken partitioning algorithms.
Sample partitions with QuickSort
Either of the above will work. A few slight modifications to the functions to produce output when a swap happens and sorting a randomly shuffled array of values demonstrates the swap-count reduction. The following implements both algorithms in two partition functions, appropriately labeled sweep
and squeeze
. They are both turned loose on identical random sequences, then again on fully-sorted sequences to demonstrate the swap-count differences.
#include <iostream>
#include <algorithm>
#include <random>
#include <numeric>
size_t sweep(int A[], size_t len)
{
if (len < 2)
return 0;
std::iter_swap(A+len/2, A+len-1);
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i)
{
if (A[i] < A[len-1])
{
std::cout << "swap: " << A[pvt] << ',' << A[i] << '\n';
std::iter_swap(A + pvt++, A+i);
}
}
std::iter_swap(A + pvt, A+len-1);
return pvt;
}
size_t squeeze(int A[], size_t len)
{
if (len <= 1)
return 0;
std::iter_swap(A + len/2, A+len-1);
size_t low = 0, high = len;
while (1)
{
while (low < high && A[low] < A[len-1])
++low;
if (low == high--)
break;
while (low < high && !(A[high] < A[len-1]))
--high;
if (low == high)
break;
std::cout << "swap: " << A[low] << ',' << A[high] << '\n';
std::iter_swap(A+low++, A+high);
}
std::iter_swap(A+low, A+len-1);
return low;
}
void quicksort(int A[], size_t len, size_t (*part)(int[], size_t))
{
if (len < 2)
return;
size_t pvt = part(A, len);
quicksort(A, pvt++, part);
quicksort(A+pvt, len-pvt, part);
}
int main()
{
std::random_device rd;
std::mt19937 rng(rd());
int ar[31] = {0}, ar2[31];
std::iota(std::begin(ar), std::end(ar), 1);
std::shuffle(std::begin(ar), std::end(ar), rng);
std::copy(std::begin(ar), std::end(ar), std::begin(ar2));
for (auto x : ar)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Sweep Algorithm\n";
quicksort(ar, sizeof(ar)/sizeof(*ar), sweep);
for (auto x : ar)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Squeeze Algorithm\n";
quicksort(ar2, sizeof(ar2)/sizeof(*ar2), squeeze);
for (auto x : ar2)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Sweep Algorithm (sorted)\n";
quicksort(ar, sizeof(ar)/sizeof(*ar), sweep);
for (auto x : ar)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Squeeze Algorithm (sorted)\n";
quicksort(ar2, sizeof(ar2)/sizeof(*ar2), squeeze);
for (auto x : ar2)
std::cout << x << ' ';
std::cout << '\n';
}
Output (random)
8 28 21 26 10 12 17 1 11 20 30 3 18 5 24 15 9 6 13 27 31 4 16 7 19 22 14 25 29 2 23
Sweep Algorithm
swap: 8,8
swap: 28,10
swap: 21,12
swap: 26,1
swap: 28,11
swap: 21,3
swap: 17,5
swap: 26,9
swap: 28,6
swap: 20,13
swap: 30,4
swap: 21,7
swap: 18,14
swap: 17,2
swap: 8,8
swap: 10,1
swap: 12,3
swap: 10,5
swap: 11,2
swap: 12,6
swap: 10,4
swap: 11,7
swap: 8,1
swap: 3,3
swap: 5,5
swap: 7,4
swap: 3,3
swap: 4,4
swap: 3,3
swap: 7,7
swap: 13,10
swap: 12,12
swap: 13,13
swap: 12,12
swap: 23,20
swap: 26,16
swap: 28,19
swap: 23,18
swap: 27,17
swap: 20,16
swap: 20,17
swap: 20,18
swap: 16,16
swap: 30,22
swap: 24,24
swap: 30,26
swap: 31,28
swap: 30,27
swap: 26,26
swap: 27,27
swap: 26,26
swap: 30,30
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Squeeze Algorithm
swap: 28,2
swap: 21,14
swap: 26,7
swap: 17,4
swap: 20,13
swap: 30,6
swap: 18,9
swap: 14,3
swap: 7,4
swap: 14,9
swap: 12,6
swap: 30,25
swap: 27,21
swap: 31,22
swap: 23,16
swap: 25,17
swap: 30,28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Sweep Algorithm (sorted)
swap: 1,1
swap: 2,2
swap: 3,3
swap: 4,4
swap: 5,5
swap: 6,6
swap: 7,7
swap: 8,8
swap: 9,9
swap: 10,10
swap: 11,11
swap: 12,12
swap: 13,13
swap: 14,14
swap: 15,15
swap: 1,1
swap: 2,2
swap: 3,3
swap: 4,4
swap: 5,5
swap: 6,6
swap: 7,7
swap: 1,1
swap: 2,2
swap: 3,3
swap: 1,1
swap: 5,5
swap: 9,9
swap: 10,10
swap: 11,11
swap: 9,9
swap: 13,13
swap: 17,17
swap: 18,18
swap: 19,19
swap: 20,20
swap: 21,21
swap: 22,22
swap: 23,23
swap: 17,17
swap: 18,18
swap: 19,19
swap: 17,17
swap: 21,21
swap: 25,25
swap: 26,26
swap: 27,27
swap: 25,25
swap: 29,29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Squeeze Algorithm (sorted)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Best of luck.
Upvotes: 1
Reputation: 5291
Here is is an implementation using a partition index:
void swap(int A[], int i1, int i2){
int temp=A[i1];
A[i1]=A[i2];
A[i2]=temp;
}
int partition(int A[], int low, int high){
int partitionIndex, i, pivot;
pivot = A[high];
partitionIndex=low;
for(i=low; i < high; i++){
if(A[i]<=pivot){
swap(A,i,partitionIndex);
partitionIndex++;
}
}
swap(A,high,partitionIndex);
return partitionIndex;
}
void quickSort(int A[], int low, int high) {
if (low < high){
int ppos = partition(A, low, high);//does the swapping and returns the pivot
quickSort(A, low, ppos-1);
quickSort(A, ppos+1, high);
}
}
I could not figure out what was wrong with your code. In the middle of debugging i noticed that you edited the code in the question. Hope the above code helps you in root causing the issue.
Upvotes: 0
Reputation: 11
It seems that you're returning the value of the pivot instead of its position in the partition function.
Upvotes: 0