1. 程式人生 > >圖解大頂堆的構建、排序過程

圖解大頂堆的構建、排序過程

這兩天在複習大頂堆和小頂堆,比起兩年前的懵懵懂懂,這次理解起來就容易了一些。又翻看了一下自己之前的筆記[資料結構與演算法之PHP排序演算法(堆排序)](https://www.cnblogs.com/sunshineliulu/p/8610645.html),發現自己這次查閱資料,和之前的思路不太一樣,遂寫下這篇筆記,算是和以前的筆記做一個對照。 #### 一、什麼是堆 堆是一種非線性結構,可以把堆看作一棵二叉樹,也可以看作一個數組,即:**堆就是利用完全二叉樹的結構來維護的一維陣列**。 堆可以分為大頂堆和小頂堆。 **大頂堆**:每個結點的值都大於或等於其左右孩子結點的值。 **小頂堆**:每個結點的值都小於或等於其左右孩子結點的值。 如果是排序,**求升序**用大頂堆,**求降序**用小頂堆。 一般我們說 `topK` 問題,就可以用大頂堆或小頂堆來實現, **最大的 K 個**:小頂堆 **最小的 K 個**:大頂堆 #### 二、大頂堆的構建過程 **大頂堆的構建過程就是從最後一個非葉子結點開始從下往上調整。** **最後一個非葉子節點怎麼找**?這裡我們用陣列表示待排序序列,則最後一個非葉子結點的位置是:陣列長度/2-1。假如陣列長度為9,則最後一個非葉子結點位置是 9/2-1=3。 比較當前結點的值和左子樹的值,如果當前節點小於左子樹的值,就交換當前節點和左子樹; 交換完後要檢查左子樹是否滿足大頂堆的性質,不滿足則重新調整子樹結構; 再比較當前結點的值和右子樹的值,如果當前節點小於右子樹的值,就交換當前節點和右子樹; 交換完後要檢查右子樹是否滿足大頂堆的性質,不滿足則重新調整子樹結構; 無需交換調整的時候,則大頂堆構建完成。 畫個圖理解下,以 [3, 7, 16, 10, 21, 23] 為例: ![](https://img2020.cnblogs.com/blog/953680/202005/953680-20200531004135177-1000133948.png) 程式碼如下: ```php = 0; $i--) { //根節點小於左子樹 if (2 * $i + 1 < $len && $arr[$i] < $arr[2 * $i + 1]) { //交換根節點和左子樹的值 swap($arr, $i, 2 * $i + 1); // $temp = $arr[$i]; // $arr[$i] = $arr[2 * $i + 1]; // $arr[2 * $i + 1] = $temp; //檢查左子樹是否滿足大頂堆的性質,如果不滿足,則重新調整 if ((2 * (2 * $i + 1) + 1 < $len && $arr[2 * $i + 1] < $arr[2 * (2 * $i + 1) + 1]) || (2 * (2 * $i + 1) + 2 < $len && $arr[2 * $i + 1] < $arr[2 * (2 * $i + 1) + 2])) { buildBigHeap($arr, $len); } } //根節點小於右子樹 if (2 * $i + 2 < $len && $arr[$i] < $arr[2 * $i + 2]) { //交換根節點和右子樹的值 swap($arr, $i, 2 * $i + 2); // $temp = $arr[$i]; // $arr[$i] = $arr[2 * $i + 2]; // $arr[2 * $i + 2] = $temp; //檢查右子樹是否滿足大頂堆的性質,如果不滿足,則重新調整 if ((2 * (2 * $i + 2) + 1 < $len && $arr[2 * $i + 2] < $arr[2 * (2 * $i + 2) + 1]) || (2 * (2 * $i + 2) + 2 < $len && $arr[2 * $i + 2] < $arr[2 * (2 * $i + 2) + 2])) { buildBigHeap($arr, $len); } } } } /** * 交換兩個值 * m n 為陣列的下標 */ function swap(&$arr, $m, $n) { $temp = $arr[$m]; $arr[$m] = $arr[$n]; $arr[$n] = $temp; } ``` #### 三、大頂堆的排序過程 > 將待排序序列構造成一個大頂堆,此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就為最大值。然後將剩餘n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值,如此反覆執行,便能得到一個有序序列了。 是不是對上面這一大段文字很頭疼?其實排序過程用下面 4 步就能概括: 第 1 步:先 n 個元素的無序序列,構建成大頂堆 第 2 步:將根節點與最後一個元素交換位置,(將最大元素"沉"到陣列末端) 第 3 步:交換過後可能不再滿足大頂堆的條件,所以需要將剩下的 n-1 個元素重新構建成大頂堆 第 4 步:重複第 2 步、第 3 步直到整個陣列排序完成。 ```php /** * 交換交換根節點和陣列末尾元素的值 */ function adjustHeap(&$heap, $len) { $temp = $heap[0]; $heap[0] = $heap[$len - 1]; $heap[$len - 1] = $temp; } /** * 堆排序 */ function heapSort(&$arr) { $len = count($arr); for ($i = $len; $i >
0; $i--) { buildBigHeap($arr, $i); adjustHeap($arr, $i);