C#排序算法的比较分析

2019-12-26 12:02:09王冬梅

我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法

复制代码 void HeapAdjust(HeapType &H, int s, int m) {
  // 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,
  // 本函数调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆
  // (对其中记录的关键字而言)
  int j;
  RedType rc;
  rc = H.r[s];
  for (j=2*s; j < =m; j*=2) {   // 沿key较大的孩子结点向下筛选
    if (j < m && H.r[j].key < H.r[j+1].key) ++j; // j为key较大的记录的下标
    if (rc.key >= H.r[j].key) break;         // rc应插入在位置s上
    H.r[s] = H.r[j];  s = j;
  }
  H.r[s] = rc;  // 插入
} // HeapAdjust    
  
void HeapSort(HeapType &H) {
   // 对顺序表H进行堆排序。
   int i;
   RedType temp;
   for (i=H.length/2; i>0; --i)  // 把H.r[1..H.length]建成大顶堆
      HeapAdjust ( H, i, H.length );
      for (i=H.length; i>1; --i) {
         temp=H.r[i];
         H.r[i]=H.r[1];
         H.r[1]=temp;  // 将堆顶记录和当前未经排序子序列Hr[1..i]中
                       // 最后一个记录相互交换
         HeapAdjust(H, 1, i-1);  // 将H.r[1..i-1] 重新调整为大顶堆
      }
} // HeapSort

 

归并排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(nlog2n) 辅助空间:O(n) 稳定性:稳定
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。