二叉樹(二)-二叉堆

1.什么是二叉堆

二叉堆是一種特殊的堆,二叉堆是完全二元樹(二叉樹)或者是近似完全二元樹(二叉樹)。二叉堆有兩種:最大堆最小堆。最大堆:父結點的鍵值總是大于或等于任何一個子節點的鍵值;最小堆:父結點的鍵值總是小于或等于任何一個子節點的鍵值。

上圖說:

二叉堆

可以看出,這是一顆完全二叉樹,同時這也是一個最大堆。
值得一提的是,完全二叉樹具有以下性質,在之后的分析中會使用到:
假設節點編號從1開始,編程實現時,數組從0號開始,所以可采用占位符把0號位置占用,或者將后續所有節點全部減一。

  • 結點i的父結點為結點i/2 (注意這里是地板除法,舉個栗子,在C/JAVA...語言中 直接另1/2等于0,而不是0.5。具體可以這樣寫floor(1/2)也就是將1/2的結果在向下取整。)
  • 結點i的子結點分別為(2*i)和(2*i+1)

同時,基于完全二叉樹的特性,二叉堆通常使用數組來編寫。

2.二叉堆的基本操作

二叉堆的基本操作為插入,提取最大(最小)值。下面以最大堆作為例子來介紹,最小堆只是最大堆的鏡像反轉,所以我就不多說啦。

2.1插入

insert.gif

在上面的二叉堆中,我們插入了55這個節點。
我們來分析下,它是如何插入到這個二叉堆中的:

  • 首先按照完全二叉樹的順序,我們在編號12這個地方生成55這個節點
  • 接著,由于這是一個最大二叉堆,所以要比較它的父節點(如果有的話)和它的大小,這里55是大于7的,所以兩個交換了位置
  • 循環第2步,直到它的父親節點不比它大,或者已經達到根節點

從上面三步分析中,我們可以直到發生,一個插入的操作無非就是:插入到最后這個位置,向上更新兩步。所以我們可以寫出下面的代碼:

void insert(int e) {
        ensureSize();            //保證空間還足夠插入
        elem[length] = e;        //插入
        heapUp();                //向上更新
        length++;
    }

So,how to headUp()?

  void heapUp() {
        int i = length;
        while (hasParentIndex(i) && elem[parentIndex(i)] < elem[i]) {
            swap(elem[parentIndex(i)], elem[i]);
            i = parentIndex(i);
        }
    }

我相信已經足夠簡單了。一直向上檢查是否有比自己小的元素,只要有,就交換。

知道怎么插入節點了,那么我們能夠運用它來做什么呢?(廢話,當然是插入操作了),其實最簡單的我們可以用它來生成二叉堆。
為了加深印象,我再貼一張,生成二叉堆動態的圖:

create.gif

生成二叉堆

就隨機插入一些元素吧,randNumber是我寫的隨機函數,各位同學看自己熟悉的語言怎么實現方便就怎么實現吧。

    for (int i = 0; i < 10; i++) {
        heap.insert(randNumber(0, 1000));
    }

2.2 提取最大(最小)值

最大(最小)堆的根節點,代表了這個堆中的最大(最小)值,將它進行彈出,在把整棵樹最后的節點放置到根節點,再把它交換到恰當的位置,這就是提取操作。

表達的不是很好,所以還是貼圖吧

extract.gif

相信你已經能夠理解什么是提取操作了。其實具體就分為三步:

  1. Pop堆頂元素
  2. 把當前樹(數組)中最后一個非空元素提取上來
  3. heapDown,更新,只要遇到比自己大的元素就交換。

那么下面看代碼:

 bool pop() {
        if (!isEmpty()) {
            elem[0] = elem[length - 1];
            length--;
            //swap down
            heapDown();
            return true;
        } else {
            return false;
        }
    }
  
   void heapDown() {
        int i = 0;
        while (hasLeftChild(i)) {
            //find the minimum index of this node's child
            int largerIndex = leftSonIndex(i);
            if (hasRightChild(i) && elem[rightSonIndex(i)] > elem[leftSonIndex(i)]) {
                largerIndex = rightSonIndex(i);
            }
            if (elem[i] > elem[largerIndex]) {
                break;
            }
            swap(elem[i], elem[largerIndex]);
            i = largerIndex;
        }
    }

heapDown操作比heapUp略微復雜一點,因為從上往下時有左子樹和右子樹,我們要檢查哪個子樹更大,總是把更大的哪個往上堆,至于為什么,因為越往下越小嘛。

那么提取操作能做什么呢?沒錯,就是今天的應用-堆排序

3.二叉堆應用-堆排序

所謂的堆排序,也就是利用了二叉堆本身性質,在循環調用提取操作的一種排序方式,其平均時間復雜度為O(nlogn),是一種排序效率很高的排序方法。
還是老規矩,先上圖:

heap_sort.gif

寫個driver來測試一下:

int main(void) {
    Heap heap(1000);
    //初始化隨機種子
    srand((unsigned int) time(NULL));
    for (int i = 0; i < 100000; i++) {
        heap.insert(randNumber(0, 1000000));
    }
    while (!heap.isEmpty()) {
        cout << heap.top() << " ";
        heap.pop();
    }
    return 0;
}

本文中所有的源碼都可以在我的Github上找到:https://github.com/zzbb1199/DataStructure

除了本文,也推薦去看下這個視頻(需要去外面看看才行喲),10分鐘搞定堆,雖然是全英文的,但是講得簡短而且很清晰,學習數據結構的同時也鍛煉了自己的英語能力嘛:https://www.youtube.com/watch?v=t0Cq6tVNRBA&t=490s

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

推薦閱讀更多精彩內容

  • B樹的定義 一棵m階的B樹滿足下列條件: 樹中每個結點至多有m個孩子。 除根結點和葉子結點外,其它每個結點至少有m...
    文檔隨手記閱讀 13,289評論 0 25
  • 樹的概述 樹是一種非常常用的數據結構,樹與前面介紹的線性表,棧,隊列等線性結構不同,樹是一種非線性結構 1.樹的定...
    Jack921閱讀 4,475評論 1 31
  • 四、樹與二叉樹 1. 二叉樹的順序存儲結構 二叉樹的順序存儲就是用數組存儲二叉樹。二叉樹的每個結點在順序存儲中都有...
    MinoyJet閱讀 1,569評論 0 7
  • 第一章 緒論 什么是數據結構? 數據結構的定義:數據結構是相互之間存在一種或多種特定關系的數據元素的集合。 第二章...
    SeanCheney閱讀 5,807評論 0 19
  • 在2017年8月14的下午,我哭了,是因為我不能去我的好朋友――石頭家,讓我想起來說一下。 那天下...
    薛義之Harry閱讀 168評論 1 2