例說資料結構&STL(六)——heap

無鞋童鞋發表於2017-07-29

1 白話佇列(queue)
  heap並不歸屬於STL容器元件,不像佇列queue它們擁有自己獨立的類定義,它只能藉助其他諸如陣列,vector等資料結構完成堆的構造操作。但是heap實際當中有很重要的應用,像大家最熟悉的堆排序,所以STL中還是有對應的函式定義的。
  接觸過堆排序的知道,所謂binary heap 就是一種complete binary tree(完全二叉樹),也就是說,整棵binary tree 除了最底層的葉節點之外,是填滿的,而最底層的葉節點(s)由左至右又不得有空隙。如下圖所示:

這裡寫圖片描述

  將像上圖一樣,我們還是藉助例如陣列儲存完全二叉樹的資訊,當完全二叉樹中的某個節點位於陣列的i處時,其左子節點必位於陣列的2i處,其右子節點比位於陣列的2i+1處,其父節點必位於“i/2”處。通過這麼簡單的位置規則,陣列可以輕易實現出完全二叉樹。
  根據元素的排列方式,heap可分為最大堆(max-heap )和 最小堆(min-heap)兩種,前者每個節點的鍵值都大於或等於其子節點鍵值,後者的每個節點鍵值都小於或等於其子節點鍵值。STL預設提供的是最大堆。
2 STL中heap實戰
 2.1 包含heap的標頭檔案
  heap構建,排序,刪除元素等函式操作是定義在algorithm標頭檔案中的,其實這個標頭檔案中還包含STL很多其他非常優秀的演算法,感興趣的可以查閱相關資料自行學習,它可以讓你的程式設計事半功倍。另外一直強調的std名稱空間,實現如下:

#include<algorithm>

using namespace std;

 2.2 構建堆
  前面說過堆的構建需要藉助一個載體,本文中我們圍繞vector容器來搭建與處理堆。首先我們來看看堆的構建:

int nArr[10] = {0,2,8,9,1,4,3,7,5,6};
vector<int> vec(nArr,nArr+10);    //構建載體vector

make_heap(vec.begin(),vec.end()); //圍繞vec構建一個堆。

  上面vector初始化的時候儲存資料順序是0,2,8,9,1,4,3,7,5,6,經過堆構建處理最後vector中資料的順序變成9,7,8,5,6,4,3,2,0,1。新的vector中的資料是完全符合堆的規則的,即前者每個節點的鍵值都大於或等於其子節點鍵值,後者的每個節點鍵值都小於或等於其子節點鍵值。
 2.3 出堆操作
  此處出堆操作就是指刪除堆中根節點。下圖是 pop_heap演算法的實際操演情況。既然身為max-heap,最大值必然在根節點。pop操作取走根節點(其實是設至底部容器vector的尾端節點)後,為了滿足完全二叉樹的條件,必須割捨最下層最右邊的葉節點,並將其值重新安插至max-heap(因此有必要重新調整heap結構)。

這裡寫圖片描述

  這裡需要注意的是對vector出堆操作必須保證vector中資料是堆儲存的,也就是出堆之前一定要make_heap()操作。此外出堆之後,並不是vector中最大值被刪除,而是暫且儲存到vector中最後一位,也就是說真正刪除該數還必須在vector中刪除依次。程式如下:

pop_heap(vec.begin(),vec.end()); // 出堆根節點,將其暫存vector最後一位

vec.pop_back(); // vector刪除出堆的資料,即最後一位,現在是8,7,4,5,6,1,3,2,0

 2.4 入堆操作
  和出堆正好相反,入堆是先將數放置在堆葉節點,並且是由左向右第一個空葉節點,時刻保證是完全二叉樹的原則,然後依次上溯。上溯就是將新節點拿來與其父節點比較,如果其鍵值(key)比父節點大,就父子對換位置。如此一直上溯,直到不需對換或直到根節點為止。過程如下:

這裡寫圖片描述

  需要注意在入堆之前,一定要保證vector尾部已經插入一個新的數,而且和出堆一樣,操作之前一定要保證vector已經建堆。程式如下:

vec.push_back(50); // 先新增一個新資料

push_heap(vec.begin(),vec.end()); //再入堆操作

 2.5 堆排序操作
  堆排序操作的前提也是一樣,需要構建堆,然後我們就可以進行堆排序讓vector中的數是順序排列的,通過上面的操作最後的順序就是0,1,2,3,4,5,6,7,8,50。記住是增序。

sort_heap(vec.begin(),vec.end()); //堆排序

  這塊解釋一下為什麼是增序,實際上上面的排序實現就是依靠依次出堆的操作完成的,我們已經知道出堆都會將預設大根節點儲存在vector的末尾,所以最終所有的資料就依次在vector中是增序排列的。
3 小結
  上面詳細的介紹了heap資料結構以及STL中提供的幾個與堆有關的方法介面。由於heap的所有元素都必須遵循完全二叉樹的排列規則,所以heap不提供遍歷功能,也不提供迭代器。
  以上是個人學習記錄,由於能力和時間有限,如果有錯誤望讀者糾正,謝謝!
  轉載請註明出處:http://blog.csdn.net/FX677588/article/details/76358777


  參考博文:
  http://www.cnblogs.com/yyxt/p/4979681.html

相關文章