二叉樹(二)-二叉堆

1.什么是二叉堆

二叉堆是一種特殊的堆榜聂,二叉堆是完全二元樹(二叉樹)或者是近似完全二元樹(二叉樹)搞疗。二叉堆有兩種:最大堆最小堆。最大堆:父結(jié)點(diǎn)的鍵值總是大于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值须肆;最小堆:父結(jié)點(diǎn)的鍵值總是小于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值匿乃。

上圖說:

二叉堆

可以看出,這是一顆完全二叉樹,同時(shí)這也是一個(gè)最大堆豌汇。
值得一提的是幢炸,完全二叉樹具有以下性質(zhì),在之后的分析中會(huì)使用到:
假設(shè)節(jié)點(diǎn)編號(hào)從1開始,編程實(shí)現(xiàn)時(shí)拒贱,數(shù)組從0號(hào)開始阳懂,所以可采用占位符把0號(hào)位置占用,或者將后續(xù)所有節(jié)點(diǎn)全部減一。

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

同時(shí),基于完全二叉樹的特性葱淳,二叉堆通常使用數(shù)組來編寫钝腺。

2.二叉堆的基本操作

二叉堆的基本操作為插入,提取最大(最性薏蕖)值艳狐。下面以最大堆作為例子來介紹,最小堆只是最大堆的鏡像反轉(zhuǎn)皿桑,所以我就不多說啦毫目。

2.1插入

insert.gif

在上面的二叉堆中,我們插入了55這個(gè)節(jié)點(diǎn)诲侮。
我們來分析下镀虐,它是如何插入到這個(gè)二叉堆中的:

  • 首先按照完全二叉樹的順序,我們?cè)?strong>編號(hào)12這個(gè)地方生成55這個(gè)節(jié)點(diǎn)
  • 接著沟绪,由于這是一個(gè)最大二叉堆刮便,所以要比較它的父節(jié)點(diǎn)(如果有的話)和它的大小,這里55是大于7的绽慈,所以兩個(gè)交換了位置
  • 循環(huán)第2步恨旱,直到它的父親節(jié)點(diǎn)不比它大,或者已經(jīng)達(dá)到根節(jié)點(diǎn)坝疼。

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

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);
        }
    }

我相信已經(jīng)足夠簡(jiǎn)單了。一直向上檢查是否有比自己小的元素腿椎,只要有桌硫,就交換。

知道怎么插入節(jié)點(diǎn)了啃炸,那么我們能夠運(yùn)用它來做什么呢铆隘?(廢話,當(dāng)然是插入操作了)南用,其實(shí)最簡(jiǎn)單的我們可以用它來生成二叉堆膀钠。
為了加深印象掏湾,我再貼一張,生成二叉堆動(dòng)態(tài)的圖:

create.gif

生成二叉堆

就隨機(jī)插入一些元素吧肿嘲,randNumber是我寫的隨機(jī)函數(shù)融击,各位同學(xué)看自己熟悉的語言怎么實(shí)現(xiàn)方便就怎么實(shí)現(xiàn)吧。

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

2.2 提取最大(最婿摺)值

最大(最凶鹄恕)堆的根節(jié)點(diǎn),代表了這個(gè)堆中的最大(最蟹饩取)值拇涤,將它進(jìn)行彈出,在把整棵樹最后的節(jié)點(diǎn)放置到根節(jié)點(diǎn)誉结,再把它交換到恰當(dāng)?shù)奈恢枚焓浚@就是提取操作。

表達(dá)的不是很好惩坑,所以還是貼圖吧

extract.gif

相信你已經(jīng)能夠理解什么是提取操作了掉盅。其實(shí)具體就分為三步:

  1. Pop堆頂元素
  2. 把當(dāng)前樹(數(shù)組)中最后一個(gè)非空元素提取上來
  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略微復(fù)雜一點(diǎn)搪泳,因?yàn)閺纳贤聲r(shí)有左子樹和右子樹稀轨,我們要檢查哪個(gè)子樹更大,總是把更大的哪個(gè)往上堆,至于為什么岸军,因?yàn)樵酵略叫÷铩?/p>

那么提取操作能做什么呢奋刽?沒錯(cuò),就是今天的應(yīng)用-堆排序

3.二叉堆應(yīng)用-堆排序

所謂的堆排序艰赞,也就是利用了二叉堆本身性質(zhì)佣谐,在循環(huán)調(diào)用提取操作的一種排序方式,其平均時(shí)間復(fù)雜度為O(nlogn)方妖,是一種排序效率很高的排序方法狭魂。
還是老規(guī)矩,先上圖:

heap_sort.gif

寫個(gè)driver來測(cè)試一下:

int main(void) {
    Heap heap(1000);
    //初始化隨機(jī)種子
    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

除了本文党觅,也推薦去看下這個(gè)視頻(需要去外面看看才行喲),10分鐘搞定堆雌澄,雖然是全英文的,但是講得簡(jiǎn)短而且很清晰杯瞻,學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的同時(shí)也鍛煉了自己的英語能力嘛:https://www.youtube.com/watch?v=t0Cq6tVNRBA&t=490s

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末镐牺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子魁莉,更是在濱河造成了極大的恐慌睬涧,老刑警劉巖募胃,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異畦浓,居然都是意外死亡痹束,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門宅粥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來参袱,“玉大人,你說我怎么就攤上這事秽梅∧ㄊ矗” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵企垦,是天一觀的道長(zhǎng)环壤。 經(jīng)常有香客問我,道長(zhǎng)钞诡,這世上最難降的妖魔是什么郑现? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮荧降,結(jié)果婚禮上接箫,老公的妹妹穿的比我還像新娘。我一直安慰自己朵诫,他們只是感情好辛友,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剪返,像睡著了一般废累。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脱盲,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天邑滨,我揣著相機(jī)與錄音,去河邊找鬼钱反。 笑死掖看,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的面哥。 我是一名探鬼主播乙各,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼幢竹!你這毒婦竟也來了耳峦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤焕毫,失蹤者是張志新(化名)和其女友劉穎蹲坷,沒想到半個(gè)月后驶乾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡循签,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年级乐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片县匠。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡风科,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乞旦,到底是詐尸還是另有隱情贼穆,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布兰粉,位于F島的核電站故痊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏玖姑。R本人自食惡果不足惜愕秫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焰络。 院中可真熱鬧戴甩,春花似錦、人聲如沸闪彼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽备蚓。三九已至课蔬,卻和暖如春囱稽,著一層夾襖步出監(jiān)牢的瞬間郊尝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工战惊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留流昏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓吞获,卻偏偏與公主長(zhǎng)得像况凉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子各拷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • B樹的定義 一棵m階的B樹滿足下列條件: 樹中每個(gè)結(jié)點(diǎn)至多有m個(gè)孩子刁绒。 除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個(gè)結(jié)點(diǎn)至少有m...
    文檔隨手記閱讀 13,199評(píng)論 0 25
  • 樹的概述 樹是一種非常常用的數(shù)據(jù)結(jié)構(gòu)烤黍,樹與前面介紹的線性表知市,棧傻盟,隊(duì)列等線性結(jié)構(gòu)不同,樹是一種非線性結(jié)構(gòu) 1.樹的定...
    Jack921閱讀 4,442評(píng)論 1 31
  • 四、樹與二叉樹 1. 二叉樹的順序存儲(chǔ)結(jié)構(gòu) 二叉樹的順序存儲(chǔ)就是用數(shù)組存儲(chǔ)二叉樹跟啤。二叉樹的每個(gè)結(jié)點(diǎn)在順序存儲(chǔ)中都有...
    MinoyJet閱讀 1,512評(píng)論 0 7
  • 第一章 緒論 什么是數(shù)據(jù)結(jié)構(gòu)诽表? 數(shù)據(jù)結(jié)構(gòu)的定義:數(shù)據(jù)結(jié)構(gòu)是相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。 第二章...
    SeanCheney閱讀 5,754評(píng)論 0 19
  • 在2017年8月14的下午隅肥,我哭了竿奏,是因?yàn)槲也荒苋ノ业暮门笥穴D―石頭家,讓我想起來說一下武福。 那天下...
    薛義之Harry閱讀 157評(píng)論 1 2