10.4.2 堆排序

  如何"建堆"?

  建堆的过程是一个"从下到上"调整堆的过程。显然,叶子结点是个堆,对记录无序系列中"最后一个"分支结点而言,满足筛选的前提,即除根结点之外,其左、右子树都是堆,由此可调用算法10.13将它调整为一个堆。类似地,"从后往前"看每个记录都满足筛选的前提,依次进行调整直至对以第1个记录为根的"二叉树"进行筛选之后,整个记录序列就是一个大顶堆了。例如右侧动画所示为对前述记录无序序列进行建堆的过程。

算法 10.14
  void HeapSort ( SqList &H )
 {
  // 对顺序表H进行堆排序
  for ( i=H.length/2; i>0; --i )  // 将 H.r[1..H.length] 建成大顶堆
   HeapAdjust ( H, i, H.length );
  H.r[1] ←→ H.r[H.length];    // 互换"堆顶"和"堆底"的记录
  for ( i=H.length-1; i>1; --i ) {
   HeapAdjust(H, 1, i);// 从根开始调整,将 H.r[1..i] 重新调整为大顶堆
   H.r[1] ←→ H.r[i];
         // 互换"堆顶"和当前的"堆底",使已有序的记录沉积在底部
  }//for
 } // HeapSort

  可以证明,堆排序的时间复杂度O (nlogn)。和选择排序雷同,无论待排序列中的记录是正序还是逆序排列,都不会使堆排序处于"最好"或"最坏"的状态。
 



动画

  算法10.14中第一个 for 循环调用 n/2 次筛选算法完成了建大顶堆的操作,选出序列中最大关键字记录,算法中第二个 for 循环调用 n-2 次筛选算法依次选出序列中第2大,第3大,…,第 n-1 大关键字的记录,最后被交换到 H.r[1] 位置上的记录必为关键字最小记录。

  时间复杂度的详细推导请参见教材所述。