最近面試,老是被問到堆排序算法。
回答時老是感覺思路不清楚,現在總結一下,把思路弄清楚的。
1.堆排序是利用堆的特性對記錄序列進行排序的一種排序方法。
好的那麼堆得特性是什麼呢?
堆得定義:
好的那麼堆得特性是什麼呢?
堆得定義:
堆是滿足下列性質的數列{r1, r2, …,rn}:
如下圖最開始是一個小頂堆。當把97和13 交換後不是堆了,所以我們要調整根節點使之成爲堆即篩選。(注意:是自堆頂到葉子的篩選過程,應該剛開始是堆由於把堆頂給換了,罪魁禍首是堆頂,其它小範圍還是堆,所以是從堆頂開始)。
這其中還要注意一點。97 與13 交換後應該跟27 比較爲什麼呢?
1.因爲是小頂堆,所以在97 的子節點裏選擇小者。如果把38放上去。38成了27的父節點比27大就不是小頂堆了。如果換成大頂堆就要比較把大的數據放上去。
所以程序裏交換時要先要比較一下。
程序如下:
- //堆調整算法
- void HeapAdjust (HeapType &H, int s, int m)
- { // 已知 H.r[s..m]中記錄的關鍵字除 H.r[s] 之外
- //均滿足堆的特徵,本函數自上而下調整 H.r[s]
- //的關鍵字,使 H.r[s..m] 也成爲一個大頂堆
- rc = H.r[s]; // 暫存 H.r[s]
- for ( j=2*s; j<=m; j*=2 ) { // j 初值指向左孩子
- 自上而下的篩選過程;
- }
- // 自上而下的篩選過程
- if ( j<m && H.r[j].key>H.r[j+1].key ) ++j;
- // 左/右“子樹根”之間先進行相互比較
- // 令 j 指示關鍵字較小記錄的位置
- if ( rc.key <= H.r[j].key ) break;
- // 再作“根”和“子樹根”之間的比較,
- // 若“>=”成立,則說明已找到 rc 的插
- // 入位置 s ,不需要繼續往下調整
- H.r[s] = H.r[j]; s = j;
- // 否則記錄上移,尚需繼續往下調整
- H.r[s] = rc; // 將調整前的堆頂記錄插入到 s (注意插入的位置爲s j=2*s)
- } // HeapAdjust
2)建堆是一個從下往上進行“篩選”的過程 (首先要把底部的建成小堆,前面調整是因爲只有堆頂,其它都已經是堆了。當我建堆到堆頂是也是從堆頂往下篩選)(所以說建堆大範圍是從下往上篩選,在添加該結點時,還得從該節點往下篩選確保添加該節點後還是堆)。
如下圖建堆過程: 從97 開始->65->38 ->49這是從下往上(大範圍從下往上)。第二個圖到65時又 65與13 調整了(從上往下調整)。當到49時也是49<-> 13 <-> 27所以也是從上之下調整(爲了確保加入該結點後還是堆)。
程序如下:
堆排序算法如下:
note: 堆排序算法以前看過幾遍老是忘,問得時候思路不太清晰。只要把關鍵幾個點弄清楚,把思路搞清楚了以後就不怕了。
如下圖建堆過程: 從97 開始->65->38 ->49這是從下往上(大範圍從下往上)。第二個圖到65時又 65與13 調整了(從上往下調整)。當到49時也是49<-> 13 <-> 27所以也是從上之下調整(爲了確保加入該結點後還是堆)。
程序如下:
堆排序算法如下:
- void HeapSort ( HeapType &H ) {
- // 對順序表 H 進行堆排序
- for ( i=H.length/2; i>0; --i )
- HeapAdjust ( H.r, i, H.length ); // 建小頂堆
- for ( i=H.length; i>1; --i ) {
- H.r[1]←→H.r[i];
- // 將堆頂記錄和當前未經排序子序列
- // H.r[1..i]中最後一個記錄相互交換
- HeapAdjust(H.r, 1, i-1); // 對 H.r[1] 進行篩選
- }
- } // HeapSort
note: 堆排序算法以前看過幾遍老是忘,問得時候思路不太清晰。只要把關鍵幾個點弄清楚,把思路搞清楚了以後就不怕了。