堆排序

1. 優(yōu)先隊(duì)列

說(shuō)堆排序之前藏杖,我們要從一種特殊的數(shù)據(jù)結(jié)構(gòu)——優(yōu)先隊(duì)列說(shuō)起黎比。
優(yōu)先隊(duì)列最大的兩個(gè)特征:插入元素和刪除最大元素
優(yōu)先隊(duì)列的實(shí)現(xiàn)方式有很多種墨闲。
我們希望插入元素和刪除最大元素的時(shí)間復(fù)雜度都能降低到最低氏涩,比如1酬诀。

數(shù)據(jù)結(jié)構(gòu) 插入元素 刪除最大元素
有序數(shù)組 N 1
無(wú)序數(shù)組 1 N
logN logN
理想情況 1 1

先說(shuō)結(jié)論:用堆的方式來(lái)實(shí)現(xiàn)曹质,可以保證插入元素和刪除最大元素的時(shí)間復(fù)雜度都為logN

2. 堆

堆的定義:二叉堆婴噩,一種二叉樹(shù)的結(jié)構(gòu),又分為大二叉堆和小二叉堆羽德,我們此處主要講大二叉堆

特點(diǎn):二叉樹(shù)的父節(jié)點(diǎn)都大于兩個(gè)子節(jié)點(diǎn)几莽,根節(jié)點(diǎn)是二叉樹(shù)中最大的節(jié)點(diǎn)。

對(duì)于節(jié)點(diǎn)a[i]而言宅静,它的父節(jié)點(diǎn)為a[(i-1)/2]章蚣,它的子節(jié)點(diǎn)(如果有的話)為a[2i+1]和a[2i+2]

堆的兩種關(guān)鍵算法

有時(shí),我們會(huì)發(fā)現(xiàn)有些節(jié)點(diǎn)不符合二叉堆的規(guī)則姨夹,那可以使用上浮或者下沉的算法纤垂,將元素放到合適的位置上去矾策、

上浮(針對(duì)子節(jié)點(diǎn))

上浮算法:找到父節(jié)點(diǎn)(a[(k-1)/2])峭沦,如果比父節(jié)點(diǎn)大贾虽,則和父節(jié)點(diǎn)交換;完成一次上浮之后吼鱼,繼續(xù)和當(dāng)前的父節(jié)點(diǎn)比較榄鉴,直到比父節(jié)點(diǎn)小為止。



代碼如下:

private void heap_swim(Comparable[] a, int k) {
    while (k > 0 && less(a[(k - 1) / 2], a[k])) {
        exchange(a, (k - 1) / 2, k);
        k = (k - 1) / 2;
    }
}
下沉(針對(duì)父節(jié)點(diǎn))

下沉算法:找到子節(jié)點(diǎn)(a[2k+1]和a[2k+2])中比較大的一個(gè)蛉抓,如果比子節(jié)點(diǎn)小庆尘,則和子節(jié)點(diǎn)交換;完成一次下沉后巷送,繼續(xù)與當(dāng)前的子節(jié)點(diǎn)比較驶忌,直到比子節(jié)點(diǎn)大為止。



代碼如下

private void heap_sink(Comparable[] a, int k, int n) {
    while ((2 * k + 1) < n) {
        int j = 2 * k + 1; 
        if ((j < n) && less(a[j], a[j + 1])) {
                j = j + 1; 
        }
        if (less(a[k], a[j])) {
            exchange(a, k, j);
            k = j;
        } else {
            break;
        }
    }
}

堆的操作

堆的初始化

堆的初始化有兩套方案:

  1. 順序從左到右對(duì)每個(gè)元素進(jìn)行上浮操作(把每個(gè)元素都當(dāng)成新插入的元素)笑跛。由于第1個(gè)元素(a[0])開(kāi)始并沒(méi)有根節(jié)點(diǎn)付魔,所以,循環(huán)可以從第二個(gè)元素(a[1])開(kāi)始
for (int k = 1; k < a.length; k++){
    heap_swim(a, k);
}
  1. 逆序從右往左對(duì)每個(gè)元素進(jìn)行下沉操作飞蹂。由于節(jié)點(diǎn)a[(n-1)/2]之后的節(jié)點(diǎn)都是子節(jié)點(diǎn)几苍,所以循環(huán)可以從a[(n-1)/2]開(kāi)始
for (int k = (n - 1) / 2; k >= 0; k--) {
    heap_sink(a, k, n);
}

下沉算法的方案相對(duì)循環(huán)較少,一般堆的初始化都是采用方案二陈哑。

堆插入元素

堆的插入元素過(guò)程就是一個(gè)元素上浮的過(guò)程

堆刪除最大元素

堆的刪除最大元素分兩步:

  1. 將根節(jié)點(diǎn)元素與最后一個(gè)子節(jié)點(diǎn)元素交換妻坝,然后刪除最后一個(gè)節(jié)點(diǎn),
  2. 根節(jié)點(diǎn)元素下沉


堆排序

前面已經(jīng)搞清楚了優(yōu)先隊(duì)列惊窖,又搞清楚了堆的結(jié)構(gòu)刽宪,以及堆的上浮和下沉算法。下面我們就來(lái)研究堆排序了界酒。

堆排序的步驟:

  1. 將數(shù)組中的數(shù)據(jù)初始化為二叉堆(一般使用下沉算法來(lái)實(shí)現(xiàn))
  2. 取出二叉堆的根節(jié)點(diǎn)并和數(shù)組尾節(jié)點(diǎn)交換圣拄,這樣最大值就放到了a[n-1]。
  3. 現(xiàn)在a[0]不符合二叉堆的規(guī)則毁欣,使用下沉算法將a[0]的元素放到合適的節(jié)點(diǎn)庇谆。然后取出最大值放到a[n-2]
  4. 依次循環(huán)2.3步,直到所有二叉堆中所有元素取完凭疮。
private void heap_sort(Comparable[] a) {
    int n = a.length - 1;
    for (int k = (n - 1) / 2; k >= 0; k--) {
        heap_sink(a, k, n);
    }
    while (n > 0) {
        exchange(a, 0, n--);
        heap_sink(a, 0, n);
    }
}

堆排序的時(shí)間復(fù)雜度:nLgN

參考資料:《算法 第四版》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饭耳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哭尝,更是在濱河造成了極大的恐慌哥攘,老刑警劉巖剖煌,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件材鹦,死亡現(xiàn)場(chǎng)離奇詭異逝淹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)桶唐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門栅葡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人尤泽,你說(shuō)我怎么就攤上這事欣簇。” “怎么了坯约?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵熊咽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我闹丐,道長(zhǎng)横殴,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任卿拴,我火速辦了婚禮衫仑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘堕花。我一直安慰自己文狱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布缘挽。 她就那樣靜靜地躺著瞄崇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪壕曼。 梳的紋絲不亂的頭發(fā)上杠袱,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音窝稿,去河邊找鬼楣富。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伴榔,可吹牛的內(nèi)容都是我干的纹蝴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼踪少,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼塘安!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起援奢,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兼犯,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體切黔,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砸脊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纬霞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凌埂。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖诗芜,靈堂內(nèi)的尸體忽然破棺而出瞳抓,到底是詐尸還是另有隱情,我是刑警寧澤伏恐,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布孩哑,位于F島的核電站,受9級(jí)特大地震影響翠桦,放射性物質(zhì)發(fā)生泄漏臭笆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一秤掌、第九天 我趴在偏房一處隱蔽的房頂上張望愁铺。 院中可真熱鬧,春花似錦闻鉴、人聲如沸茵乱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瓶竭。三九已至,卻和暖如春渠羞,著一層夾襖步出監(jiān)牢的瞬間斤贰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工次询, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荧恍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓屯吊,卻偏偏與公主長(zhǎng)得像送巡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盒卸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • 數(shù)據(jù)結(jié)構(gòu)與算法--優(yōu)先隊(duì)列和堆排序 在某些數(shù)據(jù)處理的例子中骗爆,總數(shù)據(jù)量太大,無(wú)法排序(甚至無(wú)法全部裝進(jìn)內(nèi)存)蔽介。例如摘投,...
    sunhaiyu閱讀 1,031評(píng)論 0 2
  • 堆排序可以做什么 首先應(yīng)該弄清楚堆排序可以解決什么問(wèn)題煮寡,答案是顯而易見(jiàn)的:排序。說(shuō)得通俗點(diǎn)兒就是對(duì)一組無(wú)序的數(shù)字進(jìn)...
    Springlord888閱讀 30,342評(píng)論 11 52
  • 更詳細(xì)的講解和代碼調(diào)試演示過(guò)程犀呼,請(qǐng)點(diǎn)擊鏈接 做過(guò)系統(tǒng)編程的人都知道幸撕,幾乎任何系統(tǒng)都會(huì)提供一種時(shí)鐘機(jī)制,也就是Set...
    望月從良閱讀 778評(píng)論 0 1
  • 關(guān)于最大堆 什么是最大堆和最小堆?最大(欣矍Α)堆是指在樹(shù)中跃须,存在一個(gè)結(jié)點(diǎn)而且該結(jié)點(diǎn)有兒子結(jié)點(diǎn),該結(jié)點(diǎn)的data域值都...
    凌云壯志幾多愁閱讀 87,905評(píng)論 33 71
  • “上街區(qū)”名娃兽,沿自“上街火車站”名菇民,而“上街火車站”名,又源于“上街村”村名投储。 為了一個(gè)廠第练,建了一個(gè)區(qū) 上街區(qū)距今...
    瘋丫吖閱讀 932評(píng)論 0 0