Below is the partition()
logic used by qSort()
,
static void qSort(List *list, int low, int high, compareTo compare){
if(high <= low){
return; // no partition for sub array of size 1
}
int pivotIndex = partition(list, low, high, compare);
qSort(list, low, pivotIndex-1, compare);
qSort(list, pivotIndex+1, high, compare);
}
static int partition(List *list, int low, int high, compareTo compare){
int pivot = low;
int leftIndex = low + 1;
int rightIndex = high;
const void **array = list->array;
while(true){
while( leftIndex < high && (compare(array[leftIndex], array[pivot]) < 0) ){
leftIndex++;
}
while( rightIndex > pivot && (compare(array[rightIndex], array[pivot]) > 0) ){
rightIndex--;
}
if(leftIndex >= rightIndex){
break; // partition is done
}
if( compare(array[leftIndex], array[rightIndex]) == 0 ){
leftIndex++; rightIndex--;
continue; //Maintain stability
}
arraySwap(list, leftIndex, rightIndex);
}
if( compare(array[pivot], array[rightIndex]) != 0 ){
arraySwap(list, pivot, rightIndex); // Maintain stability
}
return rightIndex;
}
where arraySwap()
&& compare()
is defined as,
void arraySwap(List *list, int i, int j){
const void **array = list->array;
const void *tempPointer = array[i];
array[i] = array[j];
array[j] = tempPointer;
}
int compare(const void *key, const void *item){
if( ((Person *)key)->age < ((Person *)item)->age ){
return -1;
}else if( ((Person *)key)->age > ((Person *)item)->age ){
return 1;
}else{
return 0;
}
}
partition()
has to maintain stability by having extra checks before each arraySwap()
.
But below output shows that, stability is partially maintained(key 10
is stable unlike key 50
),
$ ./sort.exe
Before sorting
Age,LastName,FirstName
50 B A
30 A B
20 X D
10 F A
50 A B
90 V E
60 N M
10 A B
After sorting
Age,LastName,FirstName
10 F A
10 A B
20 X D
30 A B
50 A B
50 B A
60 N M
90 V E
In the partition()
function,
below code chunk is to just maintain stability,
while(true){
....
if( compare(array[leftIndex], array[rightIndex]) == 0 ){
leftIndex++; rightIndex--;
continue; //Maintain stability
}
....
}
...
if( compare(array[pivot], array[rightIndex]) != 0 ){
...
}
Question:
Why record with key 50
is not stable?
Quick-sort is unstable because the partition step may swap elements that compare equal to each other, and thus put them in a different order than in the original array.
Making quick-sort stable requires a comparison function that will always return non zero for different elements.
Avoiding swapping equal elements is not by any means sufficient to achieve a stable quicksort. Consider, for example, this simple case:
key value
2 A
3 B
3 C
1 D
1 E
Taking the first element as the pivot, the first partitioning involves three swaps: (1, 4) and (2, 3) in the main part of the partitioning, then (0, 2) to put the pivot in place. That yields:
1 D
1 E
2 A
3 C
3 B
No elements having equal keys were swapped, but the relative order of the two items with key 3
was reversed. This happens naturally, at both ends of the partition, as a result of the upper half of the array being traversed in reverse order.
Additionally, you have an opportunity for instability when you swap the pivot element into place. Since it starts at the far left of the array, if there are any other elements with the same key that end up in the left partition, but the element at the extreme right end of the left partition has a different key, then the partition element will be moved relative to others having the same key.
To be sure of a stable sort, the comparison function must take the original element order into account. That would usually require using O(N) additional metadata. It would be fair to interpret this as saying that quicksort cannot be made stable at all, since incorporating the original element order into the comparison function effectively makes all elements unequal, and the question of stability therefore moot.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With