數據結構之通俗易懂的堆

1.堆的簡介

堆(Heap)是一個可以被看成近似完全二叉樹的數組。樹上的每一個結點對應數組的一個元素。除了最底層外,該樹是完全充滿的,而且是從左到右填充。

堆包括最大堆和最小堆(又稱大小頂堆):最大堆的每一個節點(除了根結點)的值不大於其父節點;最小堆的每一個節點(除了根結點)的值不小於其父節點。

                

堆和完全二叉樹最主要的區別是堆是用數組來實現的,上圖紅色編號即爲數組下標,滿足如下關係:
最大堆:arr[i]>=arr[2*i+1]&&arr[i]>=arr[2*i+2]
最小堆:arr[i]<=arr[2*i+1]&&arr[i]<=arr[2*i+2]

2.堆常見的操作

2.1.建堆:把一個亂序的數組變成堆結構的數組,時間複雜度爲 O(n)。

不必將值一個個地插入堆中,通過交換形成堆。假設一個小根堆的左、右子樹都已是堆,並且根的元素名爲root,其左右子節點爲left和right,這種情況下,有兩種可能:
(1)root<=left&&root<=right,此時堆已完成;
(2)root>left||root>right,此時root應與兩個子女中值較小的一個交換,結果得到一個堆,如果此時root仍然大於其新子女的一個或全部的兩個,則需繼續將root向下交換,否則結束遞歸。

c++代碼如下:

void make_heap(int *a, int len){
    for(int i =  (len-1)/2; i >= 0; --i)    //遍歷每個 非葉子節點
                  adjust_heap(a, i, len);//不用考慮那麼多, 用面向對象的思鄉去考慮,   
 }                                    //這個函數的作用就是用來使當前節點的子樹符合堆的規律

void adjust_heap(int* a, int node, int size){
        int left = 2*node + 1;
        int right = 2*node + 2;  
        int max = node;
        if( left < size && a[left] > a[max])    
                max = left;
        if( right < size && a[right] > a[max])
                max = right;
        if(max != node) {   
                swap( a[max], a[node]);    //交換節點
                adjust_heap(a, max, size);     //由於交換了node和max的值,所以此時max不一定符合堆的規律,還需重新使其成堆
        }                     
}

2.2.插入:把一個數值放進已經是堆結構的數組中,並保持堆結構,時間複雜度爲 O(log n)。
具體實現爲(以小頂堆爲例):將數據放入數組最後,依次和其所對應父節點比較,如果小與父節點則與父節點交換位置。

       
2.3.刪除:從最大堆中取出最大值或從最小堆中取出最小值,並將剩餘的數組保持堆結構,時間複雜度爲 O(log n)。
具體實現爲:直接將數組裏第一個數取出,然後將最後一個數據放入第一個位置,然後將此父節點數據與兩個子節點比較,最小的數放入父節點,遞歸此步驟直到完成排序。
       
2.4.排序:藉由建堆和刪除對數組進行排序,時間複雜度爲 O(n log n),空間複雜度爲 O(1)。
具體實現爲:假如我們要使n個元素從大到小排列,則需先構建小頂堆,先將堆頂元素與最後元素交換,交換數組最後一個元素爲最小元素,然後對前n-1個元素重新構建堆(類似與前面的刪除操作)。遞歸,第i次找到第i小的元素放入數組倒數第i個位置。

3.STL中堆的使用

上述堆的操作在STL已經實現,包含在頭文件<algorithm>裏。

3.1.建堆:make_heap
void make_heap (Iterator first, Iterator last, Compare comp);對 [first,last) 範圍的數組進行建堆,如果沒有比較函數comp則默認構造最大堆。

3.2.插入:push_heap
void push_heap (Iterator first, Iterator last, Compare comp);給定一個在 [first,last-1) 範圍內的堆,此函數通過將(last-1)中的值放入堆內相應的位置,將堆的範圍擴展到 [first,last)。

3.3.刪除:pop_heap
void pop_heap (Iterator first, Iterator last, Compare comp);刪除堆的最上面一個元素,過程如下:將堆的最上面元素放到最後一個位置,即last-1處,再將數組最後一個元素刪除即可。
3.4.排序:sort_heap
void sort_heap (Iterator first, Iterator last, Compare comp);對堆進行排序

 

舉例

 

#include <iostream>     // std::cout
#include <algorithm>    // std::make_heap, std::pop_heap, std::push_heap, std::sort_heap
#include <vector>       // std::vector

int main () {
  int myints[] = {10,20,30,5,15};
  std::vector<int> v(myints,myints+5);

  std::make_heap (v.begin(),v.end());
  std::cout << "initial max heap   : " << v.front() << '\n';

  std::pop_heap (v.begin(),v.end()); v.pop_back();
  std::cout << "max heap after pop : " << v.front() << '\n';

  v.push_back(99); std::push_heap (v.begin(),v.end());
  std::cout << "max heap after push: " << v.front() << '\n';

  std::sort_heap (v.begin(),v.end());

  std::cout << "final sorted range :";
  for (unsigned i=0; i<v.size(); i++)
    std::cout << ' ' << v[i];

  std::cout << '\n';
  return 0;
}

輸出:
initial max heap : 30
max heap after pop : 20
max heap after push: 99
final sorted range : 5 10 15 20 99

 

 

 

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