轉【STL學習】堆相關算法詳解與C++編程實現(Heap)

http://blog.csdn.net/xiajun07061225/article/details/8553808


堆簡介


堆並不是STL的組件,但是經常充當着底層實現結構。比如優先級隊列(Priority Queue)等等。

堆是一種完全二叉樹,因此我們可以用數組來存儲所有節點。在這裏的實現中,採用了一個技巧:將數組中索引爲0的元素保留,設置爲極大值或者爲極小值(依據大頂堆或者小頂堆而定)。那麼當某個節點的索引是i時,其左子節點索引爲2*i,右子節點索引爲2*i+1.父節點是i/2(這裏/表示高斯符號,取整)。這種以數組表示樹的方式,我們成爲隱式表述法(implicit reprentation)。我們這裏用C++ STL中的容器vector實現替代數組的功能。



堆分爲大頂堆和小頂堆。這裏介紹並實現的是大頂堆。


堆的主要相關算法介紹


push_heap算法

此操作是向堆中添加一個節點。爲了滿足完全二叉樹的條件,新加入的元素一定放在最下面的一層作爲葉節點,並填補在由左至右的第一個空格,在這裏放在底層容器vector的end()處。

很顯然,新元素的加入很可能使得堆不在滿足大頂堆的性質---每個節點的鍵值都大於或等於其子節點的鍵值。爲了調整使得其重新滿足大頂堆的特點,在這裏執行一個上溯(percolate up)操作:將新節點與父節點比較,如果其鍵值比父節點大,就交換父子的位置,如此一直上溯,直到不需要交換或者到根節點爲止。


pop_heap算法

此操作取走根節點。對於大頂堆,取得的是堆中值最大的節點,對於小頂堆,取得的是堆中值最小的節點。STL實現並不是將這個節點直接刪除,而是將其放在底層容器vector的尾端。而原尾端的節點插入到前面的適當位置。

我們首先保存原vector尾端的節點值,然後將根節點值存儲在此處。爲了實將原尾端節點的值插入適當位置,重新構建大頂堆,我們實施如下調整堆的操作:

先執行下溯(percolate down)操作:從根節點開始將空洞節點(一開始是根節點)和較大子節點交換,並持續向下進行,直到到達葉節點爲止。然後將已保存的原容器vector尾端節點賦給這個已到達葉層的空洞節點。

注意,到這裏並沒有結束。因爲這時候可能還沒有滿足大頂堆的特性。還需要執行一次上溯操作。這樣,便重新構建了大頂堆。


make_heap算法

此操作是依據已有的各元素構建堆。

其中,各元素已經存放在底層容器vector中。

構建堆實質是一個不斷調整堆(即前面pop_heap算法中的調整堆的操作)的過程---通過不斷調整子樹,使得子樹滿足堆的特性來使得整個樹滿足堆的性質。

葉節點顯然需要調整,第一個需要執行調整操作的子樹的根節點是從後往前的第一個非葉結點。從此節點往前到根節點對每個子樹執行調整操作,即可構建堆。


sort_heap算法

堆排序算法。執行此操作之後,容器vector中的元素按照從小到大的順序排列。

構建大頂堆之後,不斷執行pop_heap算法取出堆頂的元素,即可。因爲每次取得的是最大的元素,將其放在容器vector的最尾端。所以到最後vector中的元素是從小到大排列的。


編程實現(C plus plus)


詳細代碼包括兩個文件Heap.h以及HeapTest.cpp:

Heap.h:

  1. //STL堆算法實現(大頂堆)  
  2.   
  3. //包含容器vector的頭文件:Heap用vector來存儲元素  
  4. #include <vector>  
  5. #include <iostream>  
  6. #include <functional>  
  7.   
  8. #define MAX_VALUE 999999 //某個很大的值,存放在vector的第一個位置(最大堆)  
  9.   
  10. const int StartIndex = 1;//容器中堆元素起始索引  
  11.   
  12. using namespace std;  
  13.   
  14. //堆類定義  
  15. //默認比較規則less  
  16. template <class ElemType,class Compare = less<ElemType> >  
  17. class MyHeap{  
  18. private:  
  19.     vector<ElemType> heapDataVec;//存放元素的容器  
  20.     int numCounts;//堆中元素個數  
  21.     Compare comp;//比較規則  
  22.   
  23. public:  
  24.     MyHeap();  
  25.   
  26.     vector<ElemType> getVec();  
  27.   
  28.     void initHeap(ElemType *data,const int n);//初始化操作  
  29.     void printfHeap();//輸出堆元素  
  30.     void makeHeap();//建堆  
  31.     void sortHeap();//堆排序算法  
  32.     void pushHeap(ElemType elem);//向堆中插入元素  
  33.     void popHeap();//從堆中取出堆頂的元素  
  34.     void adjustHeap(int childTree,ElemType adjustValue);//調整子樹  
  35.     void percolateUp(int holeIndex,ElemType adjustValue);//上溯操作  
  36. };  
  37.   
  38. template <class ElemType,class Compare>  
  39. MyHeap<ElemType,Compare>::MyHeap()  
  40. :numCounts(0)  
  41. {  
  42.     heapDataVec.push_back(MAX_VALUE);  
  43. }  
  44.   
  45. template <class ElemType,class Compare>  
  46. vector<ElemType> MyHeap<ElemType,Compare>::getVec()  
  47. {  
  48.     return heapDataVec;  
  49. }  
  50.   
  51. template <class ElemType,class Compare>  
  52. void MyHeap<ElemType,Compare>::initHeap(ElemType *data,const int n)  
  53. {  
  54.     //拷貝元素數據到vector中  
  55.     for (int i = 0;i < n;++i)  
  56.     {  
  57.         heapDataVec.push_back(*(data + i));  
  58.         ++numCounts;  
  59.     }  
  60. }  
  61.   
  62. template <class ElemType,class Compare>  
  63. void MyHeap<ElemType,Compare>::printfHeap()  
  64. {  
  65.     cout << "Heap : ";  
  66.     for (int i = 1;i <= numCounts;++i)  
  67.     {  
  68.         cout << heapDataVec[i] << " ";  
  69.     }  
  70.     cout << endl;  
  71. }  
  72.   
  73. template <class ElemType,class Compare>  
  74. void MyHeap<ElemType,Compare>::makeHeap()  
  75. {  
  76.     //建堆的過程就是一個不斷調整堆的過程,循環調用函數adjustHeap依次調整子樹  
  77.     if (numCounts < 2)  
  78.         return;  
  79.     //第一個需要調整的子樹的根節點多音  
  80.     int parent = numCounts / 2;  
  81.     while(1)  
  82.     {  
  83.         adjustHeap(parent,heapDataVec[parent]);  
  84.         if (StartIndex == parent)//到達根節點  
  85.             return;  
  86.   
  87.         --parent;  
  88.     }  
  89. }  
  90.   
  91. template <class ElemType,class Compare>  
  92. void MyHeap<ElemType,Compare>::sortHeap()  
  93. {  
  94.     //堆排序思路  
  95.     //每執行一次popHeap操作,堆頂的元素被放置在尾端,然後針對前面的一次再執行popHeap操作  
  96.     //依次下去,最後即得到排序結果  
  97.     while(numCounts > 0)  
  98.         popHeap();  
  99. }  
  100.   
  101. template <class ElemType,class Compare>  
  102. void MyHeap<ElemType,Compare>::pushHeap(ElemType elem)  
  103. {  
  104.     //將新元素添加到vector中  
  105.     heapDataVec.push_back(elem);  
  106.     ++numCounts;  
  107.   
  108.     //執行一次上溯操作,調整堆,以使其滿足最大堆的性質  
  109.     percolateUp(numCounts,heapDataVec[numCounts]);  
  110. }  
  111.   
  112. template <class ElemType,class Compare>  
  113. void MyHeap<ElemType,Compare>::popHeap()  
  114. {  
  115.     //將堆頂的元素放在容器的最尾部,然後將尾部的原元素作爲調整值,重新生成堆  
  116.     ElemType adjustValue = heapDataVec[numCounts];  
  117.     //堆頂元素爲容器的首元素  
  118.     heapDataVec[numCounts] = heapDataVec[StartIndex];  
  119.     //堆中元素數目減一  
  120.     --numCounts;  
  121.   
  122.     adjustHeap(StartIndex,adjustValue);  
  123. }  
  124.   
  125. //調整以childTree爲根的子樹爲堆  
  126. template <class ElemType,class Compare>  
  127. void MyHeap<ElemType,Compare>::adjustHeap(int childTree,ElemType adjustValue)  
  128. {  
  129.     //洞節點索引  
  130.     int holeIndex = childTree;  
  131.     int secondChid = 2 * holeIndex + 1;//洞節點的右子節點(注意:起始索引從1開始)  
  132.     while(secondChid <= numCounts)  
  133.     {  
  134.         if (comp(heapDataVec[secondChid],heapDataVec[secondChid - 1]))  
  135.         {  
  136.             --secondChid;//表示兩個子節點中值較大的那個  
  137.         }  
  138.   
  139.         //上溯  
  140.         heapDataVec[holeIndex] = heapDataVec[secondChid];//令較大值爲洞值  
  141.         holeIndex = secondChid;//洞節點索引下移  
  142.         secondChid = 2 * secondChid + 1;//重新計算洞節點右子節點  
  143.     }  
  144.     //如果洞節點只有左子節點  
  145.     if (secondChid == numCounts + 1)  
  146.     {  
  147.         //令左子節點值爲洞值  
  148.         heapDataVec[holeIndex] = heapDataVec[secondChid - 1];  
  149.         holeIndex = secondChid - 1;  
  150.     }  
  151.     //將調整值賦予洞節點  
  152.     heapDataVec[holeIndex] = adjustValue;  
  153.   
  154.     //此時可能尚未滿足堆的特性,需要再執行一次上溯操作  
  155.     percolateUp(holeIndex,adjustValue);  
  156. }  
  157.   
  158. //上溯操作  
  159. template <class ElemType,class Compare>  
  160. void MyHeap<ElemType,Compare>::percolateUp(int holeIndex,ElemType adjustValue)  
  161. {  
  162.     //將新節點與其父節點進行比較,如果鍵值比其父節點大,就父子交換位置。  
  163.     //如此,知道不需要對換或直到根節點爲止  
  164.     int parentIndex = holeIndex / 2;  
  165.     while(holeIndex > StartIndex && comp(heapDataVec[parentIndex],adjustValue))  
  166.     {  
  167.         heapDataVec[holeIndex] = heapDataVec[parentIndex];  
  168.         holeIndex = parentIndex;  
  169.         parentIndex /= 2;  
  170.     }  
  171.     heapDataVec[holeIndex] = adjustValue;//將新值放置在正確的位置  
  172. }  


main.cpp:

  1. #include "Heap.h"  
  2.   
  3. #include <iostream>  
  4.   
  5. using namespace  std;  
  6.   
  7. int main()  
  8. {  
  9.     const int n = 9;  
  10.     int data[n] = {0,1,2,3,4,8,9,3,5};  
  11.   
  12.     MyHeap<int> *intHeapObj = new MyHeap<int>;  
  13.   
  14.     intHeapObj->initHeap(data,n);  
  15.     intHeapObj->printfHeap();  
  16.   
  17.     intHeapObj->makeHeap();  
  18.     intHeapObj->printfHeap();  
  19.   
  20.     intHeapObj->pushHeap(7);  
  21.     intHeapObj->printfHeap();  
  22.   
  23.     intHeapObj->popHeap();  
  24.     cout << "The top of heap :" << intHeapObj->getVec().back() << endl;  
  25.     intHeapObj->getVec().pop_back();  
  26.     intHeapObj->printfHeap();  
  27.   
  28.     intHeapObj->sortHeap();  
  29.     cout << "Sorted data :";  
  30.     for (int i = 1;i <= n;++i)  
  31.         cout << intHeapObj->getVec()[i] << " ";  
  32.     cout << endl;  
  33.         delete intHeapObj;  
  34.     return 0;  
  35. }  

運行環境:Win7 + VS2008.

運行結果:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章