【问题标题】:Why Quick sort code is breaking stability?为什么快速排序代码会破坏稳定性?
【发布时间】:2017-06-03 13:43:44
【问题描述】:

下面是qSort()使用的partition()逻辑,

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;
}

其中arraySwap() && compare() 定义为,

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() 必须通过在每个 arraySwap() 之前进行额外检查来保持稳定性。

但下面的输出表明,稳定性得到了部分维护(key 10 与 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

partition() 函数中, 下面的代码块只是为了保持稳定性,

while(true){
  ....  
  if( compare(array[leftIndex], array[rightIndex]) == 0 ){
    leftIndex++; rightIndex--;
    continue;                        //Maintain stability
  }
  ....
} 
...      
if( compare(array[pivot], array[rightIndex]) != 0 ){ 
  ... 
}

问题:

为什么用密钥50记录不稳定?

【问题讨论】:

    标签: c algorithm sorting quicksort


    【解决方案1】:

    快速排序是不稳定的,因为分区步骤可能会交换彼此比较相等的元素,从而将它们放在与原始数组中不同的顺序。

    使快速排序稳定需要一个比较函数,该函数对于不同的元素总是返回非零。

    【讨论】:

    • 查询更新了额外的描述。对于您的观点,分区步骤可能会交换彼此比较相等的元素,如果您看到代码,partiotion() 不允许arraySwap(),当元素相等时。你同意吗?
    • 好吧,我有点太精确了:分区步骤不会交换比较相等的元素,但它可能会将一个元素从左侧移动到右侧,超出另一个比较等于的元素它,有效地颠倒了它们在数组中的顺序,反之亦然。
    • 等于它,你的意思是size =1子数组?我没有对size =1 子数组进行分区。更新了查询。可能我没有得到你的评论。
    • @overexchange:没有。举个简单的例子:数组包含A1 D1 C D2 A2,pivot是C,分区步骤将交换D1A2,有效地颠倒了D1D2条目的顺序。
    【解决方案2】:

    避免交换相等的元素无论如何都不足以实现稳定的快速排序。例如,考虑这个简单的案例:

    key  value
    2    A
    3    B
    3    C
    1    D
    1    E
    

    以第一个元素为枢轴,第一次分区涉及三个交换:分区主体部分的(1, 4)和(2, 3),然后是(0, 2)将枢轴放置到位。这会产生:

    1   D
    1   E
    2   A
    3   C
    3   B
    

    没有交换具有相同键的元素,但是键为3 的两项的相对顺序颠倒了。由于数组的上半部分以相反的顺序遍历,因此在分区的两端自然会发生这种情况。

    此外,当您将枢轴元素交换到位时,您可能会出现不稳定。由于它从数组的最左侧开始,如果有任何其他具有相同键的元素最终在左分区中,但左分区最右端的元素具有不同的键,则该分区元素将相对于具有相同键的其他人移动。

    为了确保排序稳定,比较函数必须考虑原始元素顺序。这通常需要使用 O(N) 额外的元数据。可以将其解释为快速排序根本无法稳定,因为将原始元素顺序合并到比较函数中有效地使所有元素不相等,因此稳定性问题没有实际意义。

    【讨论】:

    • 所以代码 here 在这种情况下不应该工作
    • 我的理解是,1) 这个关于失去相对位置的答案适用于任何不稳定的排序。 2) 如果 compare() 之后的最小元素被复制到 aux 数组,而不是 swap(),那么这种不稳定性应该不会发生,这只不过是 merge() 操作 mergeSort()
    • @overexchange,您链接的另一个答案正是我所描述的必要,以一种相当聪明的方式。我看不出它有什么不起作用的原因。
    • @overexchange,(1)也许这个答案可以扩展到其他不稳定的排序或类似的论点,但就目前而言,它特定于快速排序的细节。 (2)我不确定我是否完全遵循您描述的变体,但就我确实遵循它的程度而言,在我看来结果不再是快速排序,并且它不具有与快速排序相同的属性(例如空间复杂度)。
    • @overexchange,我应该澄清一下:我对为什么不能使快速排序稳定的解释是特定于快速排序的。另一方面,我对如何从不稳定的排序算法中获得稳定排序结果的建议是一般性的。您可以合理地将后者描述为改变数据的性质,使其不受算法不稳定性的影响。
    猜你喜欢
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    相关资源
    最近更新 更多