算法與數據結構-堆(二叉堆)

前言

堆(二叉堆),一種動態的樹型結構,一種除了底層外,完全被填滿二叉樹結構。因此,堆一般是基于數組去實現的,它不會出現數組中很多空缺的現象,而造成空間浪費。如下是一個完全二叉樹:

完全二叉樹

它可以用數組表示為[10,7,2,5,1],若以k表示當前數組的索引,那么:

  • 其父節點:floor((k-1)/2)
  • 其左孩子:2k+1
  • 其又孩子:2k+2

結合上圖,堆的性質如下:

  • 堆必須是完全二叉樹
  • 任一節點要么比其子樹節點大,要么小;
  • 根據上面性質,堆被分為最大堆(大頂堆)和最小堆(小頂堆)。

堆的主要用途:

  • 構建優先隊列;
  • 支持堆排序;
  • 快速找出集合中的最大值或最小值。

堆結構的基本操作(以最大堆為例,本文均使用vector容器存儲數組,假設vector容器的基本操作時間復雜度為Θ(1)):

  • MaxHeap:維護最大堆的性質,時間復雜度O(nlgn);
  • BuildHeap:從無序的輸入數據構造一個最大堆,時間復雜度為O(n);

堆結構的基本操作

1.MaxHeap
輸入一個數組A和一個下標i,使不滿足最大堆性質的A[i]逐級下降,直到滿足。

void MaxHeap(vector<int> &A, int i){
    int max; //存儲父節點和其子樹節點中的最大值下標
    int lef_child = 2i;      //左孩子
    int rig_child = 2i + 1;  //右孩子
    //左孩子大于父節點
    if(lef_child <= A.size() && A[lef_child] > A[i])
        max = lef_child;   
    else max = i;
    //右孩子更大
    if(rig_child <= A.size() && A[rig_child] > A[max])
        max = rig_child;  
    
    //將最大值上移至父節點
    if(max != i){
        //交換
        int temp = A[i]; A[i] = A[max]; A[max] = temp;
        //更新了數組,需繼續查看當前元素是否滿足最大堆性質
        MaxHeap(A, max);  
    }
}

因為每一個節點子樹節點數(包括孩子的孩子)至多為2n/3(n是整個樹的節點數,最壞情況即節點為根節點,且底層大于等于半滿),所以該算法的時間復雜度為T(n) <= T(2n/3) + Θ(1)通過主方法求解得T(n)=O(lgn),最后因為含n個元素的堆高為O(lgn),所以其時間復雜度又可以表示為O(h)

2.BuildHeap
可以通過自底向上的方法,從最后一個葉節點開始倒序遍歷,并調用MaxHeap判斷當前節點子樹是否滿足最大堆性質。

void BuildHeap(vector<int> &A){
    int i = A.size()/2; //最后一個非葉節點的位置
    for(i; i >= 0; i--)  //自底向上維護
        MaxHeap(A, i);
}

該算法的時間復雜度很容易通過,循環n次,每次調用MaxHeap耗費O(lgn),從而得出T(n)=O(nlgn),雖然正確,但是該算法上界還可以繼續緊確。

一個含n個元素的堆高(最底層高為0)為floor(lgn),而該堆最多包含ceil(n/2^(h+1))高度為h的節點。而一個高度為h的節點運行MaxHeap的時間復雜度為O(h),所以可以將BuildHeap的總代價表示為:

BuildHeap的總代價

構建一個優先隊列

1.什么是優先隊列
優先隊列是一種特殊的隊列,它不按先進先出的原則,而是以優先度來彈出元素。它本質是一種用來維護由一組元素構成的集合S的數據結構,其中每個元素都有一個相關的值,稱為關鍵字。和堆一樣,優先隊列也分為最大優先隊列和最小優先隊列。

2.優先隊列的相關操作
HeapTop: 返回并刪除掉當前堆頂,時間復雜度為O(lgn)。

int HeapTop(vector<int> &A){
    if(A.size() < 1) exit(0); //沒有元素    
    int max = A[0];
    //堆頂等于最后一個值,并將最后一個值彈出
    A[0] = A[A.size()-1];
    A.push_pop();
    //維護最大堆性質
    MaxHeap(A, 0);
    return max;
}

HeapInsert:插入一個元素到當前堆中,時間復雜度為O(lgn)。

void HeapInsert(vector<int> &A, k){
    A.push_back(A[0]);
    A[0] = k;
    //維護最大堆性質
    MaxHeap(A, 0);
}

STL中堆與優先隊列的實現

1.堆
heap不屬于STL中的容器組件,它是以算法的形式呈現,“默默扮演著幕后英雄”。heap默認是最大堆排序。使用方法如下:

vector<int> ivec{0, 1, 2};
//最大堆[2, 1, 0]
make_heap(ivec.begin(), ivec.end()); 
ivec.push_back(5);
//在堆的基礎上進行數據插入[5, 2, 0, 1]
push_heap(ivec.begin(), ivec.end());
//pop_heap并沒有刪除元素,而是將堆頂元素與最后一個元素進行了替換
//[2, 1, 0, 5]
pop_heap(ivec.begin(), ivec.end());
//刪除堆頂的元素[2, 1, 0]
ivec.pop_back();
//默認小堆排序[0, 1, 2]
sort_heap(ivec.begin(), ivec.end());

2.優先隊列
priority_queue是STL中優先隊列的名稱,默認也是最大堆排序。使用方法如下:

priority_queue<int> que;
que.push(x); //每次push之后會自動維護,保證堆頂是優先級別最高的
que.pop();
que.top();

priority_queue還可以自定義存儲的數據類型以及排序方式,具體應用可以【關注公眾號DoCode,每日一道LeetCode,將零碎時間利用起來】回復關鍵字“堆”查看。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容