Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Quick sort code is breaking stability?

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?

like image 490
overexchange Avatar asked Dec 15 '22 01:12

overexchange


2 Answers

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.

like image 146
chqrlie Avatar answered Jan 06 '23 14:01

chqrlie


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.

like image 23
John Bollinger Avatar answered Jan 06 '23 12:01

John Bollinger