I am currently writing an algorithm to analyze the sorting algorithms. I have many inputs from 1000 numbers up to 1 000 000 inputs.
Currently I'm having some problems with the Quick Sort function. As I have an input of 1 000 000 of similar numbers (numbers between 1-10) this code will throw me an error (0xC00000FD) (seems to be an stack overflow exception). Now, I don't know what to do to lower the numbers of recursion calls or how to increase the stack so there could be multiple recursion calls. I'm attaching the code for the Quick Sort.
void swap(int *xp, int *yp)
{
int temp = *xp;
*xp = *yp;
*yp = temp;
}
int partition (int arr[], int low, int high)
{
int pivot = arr[(low+high)/2];
int i = (low - 1);
for (int j = low; j <= high - 1; j++)
{
if (arr[j] < pivot)
{
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
void quicksort(int A[], int l, int h)
{
if (l < h) {
int p = partition(A, l, h);
quicksort(A, l, p - 1);
quicksort(A, p + 1, h);
}
}
If you get stack overflows during recursion, it means that your recursion is broken. Recursion in general should be avoided since it has a huge potential for creating slow and dangerous algorithms. If you are a beginner programmer, then I would strongly advise to simply forget that you ever heard about recursion and stop reading here.
The only time it can be reasonably allowed is when the recursive call is placed at the end of the function, so-called "tail call recursion". This is pretty much the only form of recursion that the compiler can actually optimize and replace with an inlined loop.
If it cannot perform tail-call optimization, then it means that a function is actually called each time you do recursion. Meaning that the stack keeps piling up and you also get function call overhead. This is both needlessly slow and unacceptably dangerous. All recursive functions you ever write must therefore be disassembled for the target, to see that the code has not gone haywire.
Since this code seems to be taken from this site https://www.geeksforgeeks.org/iterative-quick-sort/, they already described most of these problems with the code for you there. They have a "quickSortIterative" function at the bottom which is a much better implementation.
My take is that the aim of the tutorial is to show you some broken code (the code in your question) then demonstrate how to write it correctly, by getting rid of the recursion.
Stack overflow can be avoided by only recursing on the smaller partition:
void quicksort(int A[], int l, int h)
{
while (l < h) {
int p = partition(A, l, h);
if((p - l) <= (h - p)){
quicksort(A, l, p - 1);
l = p + 1;
} else {
quicksort(A, p + 1, h);
h = p - 1;
}
}
}
However, worst case time complexity remains at O(n^2), and the Lomuto partition scheme used in the questions code has issues with a large number of duplicate values. Hoare partition scheme doesn't have this issue (in fact more duplicates results in less time).
https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
Example code with partition logic in quicksort:
void quicksort(int a[], int lo, int hi)
{
int p;
int i, j;
while (lo < hi){
p = a[lo + (hi - lo) / 2];
i = lo - 1;
j = hi + 1;
while (1){
while (a[++i] < p);
while (a[--j] > p);
if (i >= j)
break;
swap(a+i, a+j);
}
if(j - lo < hi - j){
quicksort(a, lo, j);
lo = j+1;
} else {
quicksort(a, j+1, hi);
hi = j;
}
}
}
Related
I need to implement a quicksort algorithm that uses random pivot; I'm working with big matrices, so i can't afford the worst case.
Now, I've found this implementation that works correctly, but it uses as pivot the first element.
I've modified it to fit my scenario (I'm working with Sparse Matrices, and I need to sort the elements by "row index, col index") and this is what I have:
void quicksortSparseMatrix(struct sparsematrix *matrix,int first,int last){
int i, j, pivot, temp_I, temp_J;
double temp_val;
if(first<last){
pivot=first; //(rand() % (last - first + 1)) + first;
i=first;
j=last;
while(i<j){
while(lessEqual(matrix,i, pivot)&&i<last)
i++;
while(greater(matrix,j, pivot))
j--;
if(i<j){
temp_I = matrix->I[i];
temp_J = matrix->J[i];
temp_val = matrix->val[i];
matrix->I[i] = matrix->I[j];
matrix->J[i] = matrix->J[j];
matrix->val[i] = matrix->val[j];
matrix->I[j]=temp_I;
matrix->J[j]=temp_J;
matrix->val[j]=temp_val;
}
}
temp_I = matrix->I[pivot];
temp_J = matrix->J[pivot];
temp_val = matrix->val[pivot];
matrix->I[pivot] = matrix->I[j];
matrix->J[pivot] = matrix->J[j];
matrix->val[pivot] = matrix->val[j];
matrix->I[j]=temp_I;
matrix->J[j]=temp_J;
matrix->val[j]=temp_val;
quicksortSparseMatrix(matrix,first,j-1);
quicksortSparseMatrix(matrix,j+1,last);
}
}
Now, the problem is that some of the matrices i'm working with are almost sorted and the algorithm runs extremely slow. I want to modify my algorithm to make it use random pivot, but if I apply the change you see commented in the code above pivot=(rand() % (last - first + 1)) + first;, the algorithm does not sort the data correctly.
Can anyone help me figure out how to change the algorithm to use a random pivot and sort the data correctly?
EDIT: this is the struct sparsematrix definition, I don't think you need it, but for completeness...
struct sparsematrix {
int M, N, nz;
int *I, *J;
double *val;
};
Pivot should be a value, not an index. The first comparison should be lessthan (not lessthanorequal), which will also eliminate the need for checking for i < last . After swapping, there should be i++ and j-- . The last two lines should be quicksortSparseMatrix(matrix,first,j); and quicksortSparseMatrix(matrix,i,last); , for this variation of Hoare partition scheme. Example code for array:
void QuickSort(int *a, int lo, int hi)
{
int i, j;
int p, t;
if(lo >= hi)
return;
p = a[lo + 1 + (rand() % (hi - lo))];
i = lo;
j = hi;
while (i <= j){
while (a[i] < p)i++;
while (a[j] > p)j--;
if (i > j)
break;
t = a[i];
a[i] = a[j];
a[j] = t;
i++;
j--;
}
QuickSort(a, lo, j);
QuickSort(a, i, hi);
}
A merge sort on an array of indexes to rows of matrix may be faster: more moves of the indexes, but fewer compares of rows of matrix. A second temp array of indexes will be needed for merge sort.
This is an attempt to make heapsort building the heap solely with function calls. The problem is that it's taking too long to complete, longer than a simpler bubblesort written in the same project, nearly 100 seconds vs bubble's 42 with 100000 entries in descending order. Mergesort and quicksort never got past a second. Nevermind the fact that it crashes on 100000 if the compiler fails to make a tail-call optimization.
It used to crash, some of the details on the implementations are there to make tail-calls happen. It has been tested on descending, ascending and randomly distributed data. Some of the changes were also made to remedy how slow it was, what gives it it's obfuscated look.
int heap_sort_step(int v[], int len, int i){
int nextl = 2 * i + 1, nextr = nextl + 1;
char bfl = (nextl<len)|((nextr<len)<<1);
switch(bfl)
{
case 3:
case 2:
while(v[i] > heap_sort_step(v, len, nextr))
swap(v + i, v + nextr);
case 1:
while(v[i] > heap_sort_step(v, len, nextl))
swap(v + i, v + nextl);
default:
return v[i];
}
}
void heap_sort(int v[], int len){
return (len > 1)?
(heap_sort_step(v, len, 0),
heap_sort(v + 1, len - 1)):
NULL;
}
heap_sort(int v[], int len) takes an array and it's size and builds the min heap for every member in the array using heap_sort_step(), ordering it. Tail calls needed here.
heap_sort_step(int v[], int len, int i) takes an array, it's size and an index for building the heap, with equations as seen in nextl and nextr, 2i+1 and 2i+2, starting with i = 0. 'bfl' is an optimization trick(minor improvements over using ifs) to decide which branches to go to or just return the current value by seeing if there are more items ahead. The switch uses fallthrough and is where the heap is built, 3 and 2 means there are things to the right (0b11, and 0b10) and 1 means there are things to the left (0b01), default behaviour is returning the current number. It used to be written as:
int heap_sort_step(int v[], int len, int i){
int nextl = (((i + 1) * 2) - 1), nextr = nextl + 1;
if(nextl < len)
while(v[i] > heap_sort_step(v, len, nextl))
swap(v + i, v + nextl);
if(nextr < len)
while(v[i] > heap_sort_step(v, len, nextr))
swap(v + i, v + nextr);
return v[i];
}
As for the recursive step, it compares the current number with the returning value from the next function call, and if it is bigger, it swaps, if it is not, there is a cascade return back to the root.
At this point I'm pretty sure it's just a bad implementation and want to know if this is a complexity problem made by the changes or something else. Can it be made to compete with mergesort and quicksort using the same concept? It should be O n(log(n)), right?
Leaving aside the question of the performance impact of recursion, and of how you're relying on tail-call optimization to make the heap_sort() function non-recursive after all, your implementation does not look like heap sort. Heap sort can be described like this:
Arrange the elements to be sorted into a heap (naively, O(n log n); thoughtfully, O(n))
As long as there are at least two elements in the heap, (O(n) iterations)
Swap the head element with the last. (O(1))
Reduce the (logical) heap size by 1. (O(1))
Restore the heap condition. (O(log n) if done right)
Of course, this applies whether you're sorting in increasing order by using a max-heap or in decreasing order by using a min-heap.
As you clarified in comments, your approach is to arrange the array into a min-heap to obtain the least element, then repeat with the n - 1 element tail of the array. That's not heap sort -- it's a variation on selection sort. If well-implemented, it costs O(n2), because each heap-building step must visit at least the n / 2 non-leaf nodes, and therefore costs O(n).
After a few changes it works like a charm, here's the solution:
It's incomplete:
The heap is being formed over and over again for each item, therefore, as pointed by John this is working like a weird selection sort.
Two new functions are added to finish:
void sift(int v[], int len, int i){
int nextl = 2 * i + 1, nextr = nextl + 1;
int next = 0;
if(nextr < len)
if(v[i] > v[nextr]){
if(v[nextr] < v[nextl])
swap(v + i, v + (next = nextr));
else
swap(v + i, v + (next = nextl));
}
if(nextl < len)
if(v[i] > v[nextl])
swap(v + i, v + (next = nextl));
return (next == 0) ? NULL: sift_min(v, len, next);
}
restores the heap. After switching the first and the last it swaps every smaller number down the tree following a single path.
void reverse(int v[], int len){
int i;
for(i = 0; i < len/2; i++)
swap((v + i), (v + (len - (i + 1))));
}
reverses the array after sorting because without knowing about the second step, using a minheap, the sorting occurs in descending order, so it's just there in to make it ascending.
There's an unnecessary recursion in the main block:
void heap_sort(int v[], int len){
return (len > 1)?
(heap_sort_step(v, len, 0),
heap_sort(v + 1, len - 1)):
NULL;
}
Gets replaced with
void heap_sort(int v[], int len){
heapify(v, len, 0);//this is heap_sort_step with a new name
int templen = len
while(templen > 1){
swap(v, v + --templen);
sift(v, templen, 0);
}
reverse(v, len);
}
The final code is:
void swap(int* ref1, int* ref2){
int temp = *ref1;
*ref1 = *ref2;
*ref2 = temp;
}
int heapify(int v[], int len, int i){
int nextl = 2 * i + 1, nextr = nextl + 1;
if(nextr < len)
while(v[i] > heapify(v, len, nextr))
swap(v + i, v + nextr);
if(nextl < len)
while(v[i] > heapify(v, len, nextl))
swap(v + i, v + nextl);
return v[i];
}
void sift(int v[], int len, int i){
int nextl = 2 * i + 1, nextr = nextl + 1;
int next = 0;
if(nextr < len)
if(v[i] > v[nextr]){
if(v[nextr] < v[nextl])
swap(v + i, v + (next = nextr));
else
swap(v + i, v + (next = nextl));
}
if(nextl < len)
if(v[i] > v[nextl])
swap(v + i, v + (next = nextl));
return (next == 0) ? NULL: sift(v, len, next);
}
void reverse(int v[], int len){
int i;
for(i = 0; i < len/2; i++)
swap((v + i), (v + (len - (i + 1))));
}
void heap_sort(int v[], int len){
heapify(v, len, 0);
int templen = len;
while(templen > 1){
swap(v, v + (--templen));
sift(v, templen, 0);
}
reverse(v, len);
}
Lengthy, but it still builds the heap recursively and is fast. Can be made faster using maxheap, and of course, eliminating recursion. A quick test yields an average of less than 0.1 seconds for the same samples that were taking more than a minute.
I found this post How to do iterative quicksort without using stack in c?
but the answer suggested does use a inline stack array! (Only constant amount of extra space is permitted)
The code in the page in reference makes a bold claim:
STACK My implementation does not use the stack to store data...
Yet the function definition has many variables with automatic storage, among them 2 arrays with 1000 entries, which will end up using a fixed but substantial amount of stack space:
// quickSort
//
// This public-domain C implementation by Darel Rex Finley.
//
// * Returns YES if sort was successful, or NO if the nested
// pivots went too deep, in which case your array will have
// been re-ordered, but probably not sorted correctly.
//
// * This function assumes it is called with valid parameters.
//
// * Example calls:
// quickSort(&myArray[0],5); // sorts elements 0, 1, 2, 3, and 4
// quickSort(&myArray[3],5); // sorts elements 3, 4, 5, 6, and 7
bool quickSort(int *arr, int elements) {
#define MAX_LEVELS 1000
int piv, beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R ;
beg[0]=0; end[0]=elements;
while (i>=0) {
L=beg[i]; R=end[i]-1;
if (L<R) {
piv=arr[L]; if (i==MAX_LEVELS-1) return NO;
while (L<R) {
while (arr[R]>=piv && L<R) R--; if (L<R) arr[L++]=arr[R];
while (arr[L]<=piv && L<R) L++; if (L<R) arr[R--]=arr[L]; }
arr[L]=piv; beg[i+1]=L+1; end[i+1]=end[i]; end[i++]=L; }
else {
i--; }}
return YES; }
The indentation style is very confusing. Here is a reformatted version:
#define MAX_LEVELS 1000
bool quickSort(int *arr, int elements) {
int piv, beg[MAX_LEVELS], end[MAX_LEVELS], i = 0, L, R;
beg[0] = 0;
end[0] = elements;
while (i >= 0) {
L = beg[i];
R = end[i] - 1;
if (L < R) {
piv = arr[L];
if (i == MAX_LEVELS - 1)
return NO;
while (L < R) {
while (arr[R] >= piv && L < R)
R--;
if (L < R)
arr[L++] = arr[R];
while (arr[L] <= piv && L < R)
L++;
if (L < R)
arr[R--] = arr[L];
}
arr[L] = piv;
beg[i + 1] = L + 1;
end[i + 1] = end[i];
end[i++] = L;
} else {
i--;
}
}
return YES;
}
Note that 1000 is large but not sufficient for pathological cases on moderately large arrays that are already sorted. The function returns NO on such arrays with a size of 1000 only, which is unacceptable.
A much lower value would suffice with an improved version of the algorithm where the larger range is pushed into the array and the loop iterates on the smaller range. This ensures that an array of N entries can handle a set of 2N entries. It still has quadratic time complexity on sorted arrays but at least would sort arrays of all possible sizes.
Here is a modified and instrumented version:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_LEVELS 64
int quickSort(int *arr, size_t elements) {
size_t beg[MAX_LEVELS], end[MAX_LEVELS], L, R;
int i = 0;
beg[0] = 0;
end[0] = elements;
while (i >= 0) {
L = beg[i];
R = end[i];
if (L + 1 < R--) {
int piv = arr[L];
if (i == MAX_LEVELS - 1)
return -1;
while (L < R) {
while (arr[R] >= piv && L < R)
R--;
if (L < R)
arr[L++] = arr[R];
while (arr[L] <= piv && L < R)
L++;
if (L < R)
arr[R--] = arr[L];
}
arr[L] = piv;
if (L - beg[i] > end[i] - R) {
beg[i + 1] = L + 1;
end[i + 1] = end[i];
end[i++] = L;
} else {
beg[i + 1] = beg[i];
end[i + 1] = L;
beg[i++] = L + 1;
}
} else {
i--;
}
}
return 0;
}
int testsort(int *a, size_t size, const char *desc) {
clock_t t = clock();
size_t i;
if (quickSort(a, size)) {
printf("%s: quickSort failure\n", desc);
return 1;
}
for (i = 1; i < size; i++) {
if (a[i - 1] > a[i]) {
printf("%s: sorting error: a[%zu]=%d > a[%zu]=%d\n",
desc, i - 1, a[i - 1], i, a[i]);
return 2;
}
}
t = clock() - t;
printf("%s: %zu elements sorted in %.3fms\n",
desc, size, t * 1000.0 / CLOCKS_PER_SEC);
return 0;
}
int main(int argc, char *argv[]) {
size_t i, size = argc > 1 ? strtoull(argv[1], NULL, 0) : 1000;
int *a = malloc(sizeof(*a) * size);
if (a != NULL) {
for (i = 0; i < size; i++)
a[i] = rand();
testsort(a, size, "random");
for (i = 0; i < size; i++)
a[i] = i;
testsort(a, size, "sorted");
for (i = 0; i < size; i++)
a[i] = size - i;
testsort(a, size, "reverse sorted");
for (i = 0; i < size; i++)
a[i] = 0;
testsort(a, size, "constant");
free(a);
}
return 0;
}
Output:
random: 100000 elements sorted in 7.379ms
sorted: 100000 elements sorted in 2799.752ms
reverse sorted: 100000 elements sorted in 2768.844ms
constant: 100000 elements sorted in 2786.612ms
Here is a slighlty modified version more resistant to pathological cases:
#define MAX_LEVELS 48
int quickSort(int *arr, size_t elements) {
size_t beg[MAX_LEVELS], end[MAX_LEVELS], L, R;
int i = 0;
beg[0] = 0;
end[0] = elements;
while (i >= 0) {
L = beg[i];
R = end[i];
if (R - L > 1) {
size_t M = L + ((R - L) >> 1);
int piv = arr[M];
arr[M] = arr[L];
if (i == MAX_LEVELS - 1)
return -1;
R--;
while (L < R) {
while (arr[R] >= piv && L < R)
R--;
if (L < R)
arr[L++] = arr[R];
while (arr[L] <= piv && L < R)
L++;
if (L < R)
arr[R--] = arr[L];
}
arr[L] = piv;
M = L + 1;
while (L > beg[i] && arr[L - 1] == piv)
L--;
while (M < end[i] && arr[M] == piv)
M++;
if (L - beg[i] > end[i] - M) {
beg[i + 1] = M;
end[i + 1] = end[i];
end[i++] = L;
} else {
beg[i + 1] = beg[i];
end[i + 1] = L;
beg[i++] = M;
}
} else {
i--;
}
}
return 0;
}
Output:
random: 10000000 elements sorted in 963.973ms
sorted: 10000000 elements sorted in 167.621ms
reverse sorted: 10000000 elements sorted in 167.375ms
constant: 10000000 elements sorted in 9.335ms
As a conclusion:
yes quick sort can be implemented without recursion,
no it cannot be implemented without any local automatic storage,
yes only a constant amount of extra space is necessary, but only because we live is a small world where the maximum size of the array is bounded by available memory. A size of 64 for the local objects handles arrays larger than the size of the Internet, much larger than current 64-bit systems could address.
Apparently, it is possible to implement a non-recursive quicksort with only constant amount of extra space as stated here. This builds upon the Sedgewick's work for non-recursive formulation of quicksort. Instead of preserving the boundary values(low and high) it essentially performs a linear scan to determine these bounds.
Can quicksort be implemented in C without stack and recursion?
Quicksort requires two paths be followed forward from each non-trivial partitioning: a new partitioning of each (sub)partition. Information about the previous partitioning (the bounds of one of the resulting partitions) needs to be carried forward to each new partitioning. The question, then, is where does that information live? In particular, where does the information about one partition live while the program is working on the other?
For a serial algorithm, the answer is that the information is stored on a stack or a queue or a functional equivalent of one of those. Always, because those are our names for data structures that serve the needed purpose. In particular, recursion is a special case, not an alternative. In a recursive quicksort, the data are stored on the call stack. For an iterative implementation you can implement a stack in a formal sense, but it's possible to instead use a simple and relatively small array as a makeshift stack.
But stack and queue equivalents can go a lot farther than that. You could append data to a file, for example, for later read-back. You could write it to a pipe. You could transmit it to yourself asynchronously over a communications network.
If you're clever, you can even make the input array itself serve the need for a stack, by encoding the partition bounds using relative element order or some other element property, as described by Ďurian, for example. This involves a space vs speed tradeoff that is probably not a good deal in most cases. However, it has lower space overhead (O(1)) than do typical quicksort implementations (O(log N)), and it does not change the algorithm's O(N log N) asymptotic time complexity.
If you wanted to go crazy, you could even nest iterations in place of recursing. That would impose a hard upper bound on the size of the arrays that could be handled, but not as tight of one as you might think. With some care and a few tricks, you could handle billion-element arrays with a 25-loop nest. Such a deep nest would be ugly and crazy, but nevertheless conceivable. A human could write it by hand. And in that case, the series of nested loop scopes, with their block-scoped variables, serves as a stack equivalent.
So the answer depends on what exactly you mean by "without stack":
yes, you can use a queue instead, though it would need to have about the same capacity as there are elements to sort;
yes, you can use an array or some other kind of sequential data storage, including the input array itself, to emulate a formal stack or queue;
yes, you can encode a suitable stack equivalent directly into the structure of your program;
yes, you can probably come up with other, more esoteric versions of stacks and queues;
but no, you cannot perform a quicksort without something filling the multi-level data-storage role for which a stack or stack-equivalent is conventionally used.
Well, it can, because I implemented a quicksort in fortran IV (it was a long time ago, and before the language supported recursion - and it was for a bet). However you do need somewhere (a large array would do) to remember your state as you do individual bits of work.
It's a lot easier recursively...
Quicksort is by definition a "divide and conquer" searching algorithm, the idea is that you split the given array into smaller partitions. So you are dividing the problem into subproblems, that is easier to solve.
When using Quicksort without recursion you need a struct of some sort to store the partitions you are not using at the time.
That's why the answer of the post uses an array to make quicksort non recursive.
I'm a computer science student (just started), I was working on writing from pseudocode a randomized pivot version of Quicksort. I've written and tested it, and it all works perfectly however...
The partition part looks a bit too complicated, as it feels I have missed something or overthought it. I can't understand if it's ok or if I made some avoidable mistakes.
So long story short: it works, but how to do better?
Thanks in advance for all the help
void partition(int a[],int start,int end)
{
srand (time(NULL));
int pivotpos = 3; //start + rand() % (end-start);
int i = start; // index 1
int j = end; // index 2
int flag = 1;
int pivot = a[pivotpos]; // sets the pivot's value
while(i<j && flag) // main loop
{
flag = 0;
while (a[i]<pivot)
{
i++;
}
while (a[j]>pivot)
{
j--;
}
if(a[i]>a[j]) // swap && sets new pivot, and restores the flag
{
swap(&a[i],&a[j]);
if(pivotpos == i)
pivotpos = j;
else if(pivotpos == j)
pivotpos = i;
flag++;
}
else if(a[i] == a[j]) // avoids getting suck on a mirror of values (fx pivot on pos 3 of : 1-0-0-1-1)
{
if(pivotpos == i)
j--;
else if(pivotpos == j)
i++;
else
{
i++;
j--;
}
flag++;
}
}
}
This is the pseudo code of partition() from Introduction to Algorithms , which is called Lomuto's Partitioning Algorithm, and there's a good explanation below it in the book.
PARTITION(A, p, r)
1 x ← A[r]
2 i ← p - 1
3 for j ← p to r - 1
4 do if A[j] ≤ x
5 then i ←i + 1
6 exchange A[i] ↔ A[j]
7 exchange A[i + 1] ↔ A[r]
8 return i +1
You can implement a randomized partition implementation easily based on the pseudo code above. As the comment pointed out, move the srand() out of the partition.
// srand(time(NULL));
int partition(int* arr, int start, int end)
{
int pivot_index = start + rand() % (end - start + 1);
int pivot = arr[pivot_index ];
swap(&arr[pivot_index ], &arr[end]); // swap random pivot to end.
pivot_index = end;
int i = start -1;
for(int j = start; j <= end - 1; j++)
{
if(arr[j] <= pivot)
{
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[pivot_index]); // place the pivot to right place
return i + 1;
}
And there is another partition method mentioned in the book, which is called Hoare's Partitioning Algorithm, the pseudo code is as below:
Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
repeat
j = j - 1
until A[j] <= x
repeat
i = i + 1
until A[i] >= x
if i < j
swap( A[i], A[j] )
else
return j
After the partition, every element in A[p...j] ≤ every element in A[j+1...r]. So the quicksort would be:
QUICKSORT (A, p, r)
if p < r then
q = Hoare-Partition(A, p, r)
QUICKSORT(A, p, q)
QUICKSORT(A, q+1, r)
There are multiple ways to partition for quicksort, the following being likely the simplest I can muster. Generally two schools of partitioning are used:
The Squeeze - collapses both ends of the sequence until a suitable swap pair is found, then swaps two elements into proper sides of the partition. Not trivial to implement, but can be more efficient (reduced swap count) than the alternative...
The Sweep - uses a single left to right (or right to left) sweep the values, swapping values to an incrementing pivot index that moves as the algorithm runs. Very simple to implement, as you'll see below.
I prefer the Sweep algorithm for people learning quicksort and partitioning only because it is so dead-simple to implement. Both can be implemented to perform in-place partitioning, as is the case in the implementation below. At no time except in swap() will you see a value stored in temp-storage.
Using a random pivot selection is only a small part of this. The following shows how to initialize the random number generator, and demonstrates likely the simplest partition algorithm and quicksort usage therein you're going to find.
It demonstrates, among other things, that in C/C++, you don't need both ends of a partition since simple pointer arithmetic can be used to adjust the "top" half of a partition. See the quicksort() function for how this is done.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int *lhs, int *rhs)
{
if (lhs == rhs)
return;
int tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
int partition(int ar[], int len)
{
int i, pvt=0;
// swap random slot selection to end.
// ar[len-1] will hold the pivot value.
swap(ar + (rand() % len), ar+(len-1));
for (i=0; i<len; ++i)
{
if (ar[i] < ar[len-1])
swap(ar + i, ar + pvt++);
}
// swap the pivot value into position
swap(ar+pvt, ar+(len-1));
return pvt;
}
void quicksort(int ar[], int len)
{
if (len < 2)
return;
int pvt = partition(ar, len);
quicksort(ar, pvt++); // note increment. skips pivot slot
quicksort(ar+pvt, len-pvt);
}
int main()
{
srand((unsigned int)time(NULL));
const int N = 20;
int data[N];
for (int i=0; i<N; ++i)
{
data[i] = rand() % 50 + 1;
printf("%d ", data[i]);
}
puts("");
quicksort(data, N);
for (int i=0; i<N; ++i)
printf("%d ", data[i]);
puts("");
return 0;
}
Output (varies, obviously)
32 49 42 49 5 18 41 48 22 33 40 27 12 47 41 6 50 27 8 7
5 6 7 8 12 18 22 27 27 32 33 40 41 41 42 47 48 49 49 50
Note: this does NOT account for modulo bias for using rand() % len, and frankly it would be overkill to do so for this example. If it were critical, I would use another generator entirely. An outstanding discussion for methods of choosing random pivot locations for quicksort partitioning can be found at this post on this site, including many links to different methods. I suggest reviewing it.
I am writing a simple merge sort function to sort based on a given compar function:
void merge(int left, int mid, int right, int(*compar)(const void *, const void *))
{
// sublist sizes
int left_size = mid - left + 1;
int right_size = right - mid;
// counts
int i, j, k;
// create left and right arrays
B *left_list = (B*) malloc(left_size*sizeof(B));
B *right_list = (B*) malloc(right_size*sizeof(B));
// copy sublists, could be done with memcpy()?
for (i = 0; i < left_size; i++)
left_list[i] = list[left + i];
for (j = 0; j < right_size; j++)
right_list[j] = list[mid + j + 1];
// reset counts
i = 0; j = 0;
for (k = left; k <= right; k++)
{
if (j == right_size)
list[k] = left_list[i++];
else if (i == left_size)
list[k] = right_list[j++];
// here we call the given comparision function
else if (compar(&left_list[i], &right_list[j]) < 0)
list[k] = left_list[i++];
else
list[k] = right_list[j++];
}
}
void sort(int left, int right, int(*compar)(const void *, const void *))
{
if (left < right)
{
// find the pivot point
int mid = (left + right) / 2;
// recursive step
sort(left, mid, compar);
sort(mid + 1, right, compar);
// merge resulting sublists
merge(left, mid, right, compar);
}
}
I am then calling this several times on the same list array using different comparison functions. I am finding that the sort is stable for the first call, but then after that I see elements are swapped even though they are equal.
Can anyone suggest the reason for this behaviour?
I'm not sure if this will do it but try changing this line:
compar(&left_list[i], &right_list[j]) < 0
to this:
compar(&left_list[i], &right_list[j]) <= 0
This will make it so that if they are already equal it does the first action which will (hopefully) preserve the stability rather than moving things around.
This is just a guess though.
I think you got your sizes wrong
int left_size = mid - left;
And, as pointed by arasmussen, you need to give preference to the left list in order to mantain stability
compar(&left_list[i], &right_list[j]) <= 0
In adition to all of this, you are not calling free after malloc-ing the helper lists. This will not make the algorithm return incorrect results but will cause your program's memory use to grow irreversably everytime you call the sort function.