堆排序

二叉堆的定義

二叉堆是完全二叉樹或者是近似完全二叉樹掏导。
二叉堆滿足二個(gè)特性:

  • 父結(jié)點(diǎn)的鍵值總是大于或等于(小于或等于)任何一個(gè)子節(jié)點(diǎn)的鍵值。
  • 每個(gè)結(jié)點(diǎn)的左子樹和右子樹都是一個(gè)二叉堆(都是最大堆或最小堆)。
    當(dāng)父結(jié)點(diǎn)的鍵值總是大于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值時(shí)為最大堆密幔。當(dāng)父結(jié)點(diǎn)的鍵值總是小于或等于任何一個(gè)子節(jié)點(diǎn)的鍵值時(shí)為最小堆姆另。下圖展示一個(gè)最小堆:

    由于其它幾種堆(二項(xiàng)式堆,斐波納契堆等)用的較少孩哑,一般將二叉堆就簡稱為堆栓霜。

堆的存儲(chǔ)

一般都用數(shù)組來表示堆,i結(jié)點(diǎn)的父結(jié)點(diǎn)下標(biāo)就為(i – 1)/2横蜒。它的左右子結(jié)點(diǎn)下標(biāo)分別為2 * i + 1和2 * i + 2胳蛮。如第0個(gè)結(jié)點(diǎn)左右子結(jié)點(diǎn)下標(biāo)分別為1和2。


堆的操作——插入刪除

堆的插入

每次插入都是將新數(shù)據(jù)放在數(shù)組最后丛晌〗龃叮可以發(fā)現(xiàn)從這個(gè)新數(shù)據(jù)的父結(jié)點(diǎn)到根結(jié)點(diǎn)必然為一個(gè)有序的數(shù)列,現(xiàn)在的任務(wù)是將這個(gè)新數(shù)據(jù)插入到這個(gè)有序數(shù)據(jù)中——這就類似于直接插入排序中將一個(gè)數(shù)據(jù)并入到有序區(qū)間中.

//  新加入i結(jié)點(diǎn)  其父結(jié)點(diǎn)為(i - 1) / 2  
void MinHeapFixup(int a[], int i)  
{  
   int j, temp;  
     
   temp = a[i];  
   j = (i - 1) / 2;      //父結(jié)點(diǎn)  
   while (j >= 0 && i != 0)  
   {  
       if (a[j] <= temp)  
           break;  
         
       a[i] = a[j];     //把較大的子結(jié)點(diǎn)往下移動(dòng),替換它的子結(jié)點(diǎn)  
       i = j;  
       j = (i - 1) / 2;  
   }  
   a[i] = temp;  
}  

//在最小堆中加入新的數(shù)據(jù)nNum  
void MinHeapAddNumber(int a[], int n, int nNum)  
{  
   a[n] = nNum;  
   MinHeapFixup(a, n);  
}  

堆的刪除

按定義澎蛛,堆中每次都只能刪除第0個(gè)數(shù)據(jù)抚垄。為了便于重建堆,實(shí)際的操作是將最后一個(gè)數(shù)據(jù)的值賦給根結(jié)點(diǎn),然后再從根結(jié)點(diǎn)開始進(jìn)行一次從上向下的調(diào)整呆馁。調(diào)整時(shí)先在左右兒子結(jié)點(diǎn)中找最小的桐经,如果父結(jié)點(diǎn)比這個(gè)最小的子結(jié)點(diǎn)還小說明不需要調(diào)整了,反之將父結(jié)點(diǎn)和它交換后再考慮后面的結(jié)點(diǎn)浙滤。相當(dāng)于從根結(jié)點(diǎn)將一個(gè)數(shù)據(jù)的“下沉”過程阴挣。下面給出代碼:

//  從i節(jié)點(diǎn)開始調(diào)整,n為節(jié)點(diǎn)總數(shù) 從0開始計(jì)算 i節(jié)點(diǎn)的子節(jié)點(diǎn)為 2*i+1, 2*i+2  
void MinHeapFixdown(int a[], int i, int n)  
{  
   int j, temp;  
   temp = a[i];  
   j = 2 * i + 1;  
   while (j < n)  
   {  
       if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的  
           j++;  
       if (a[j] >= temp)  
           break;  
       a[i] = a[j];     //把較小的子結(jié)點(diǎn)往上移動(dòng),替換它的父結(jié)點(diǎn)  
       i = j;  
       j = 2 * i + 1;  
   }  
   a[i] = temp;  
}  
//在最小堆中刪除數(shù)  
void MinHeapDeleteNumber(int a[], int n)  
{  
   Swap(a[0], a[n - 1]);  
   MinHeapFixdown(a, 0, n - 1);  
}  

堆化數(shù)組

有了堆的插入和刪除后,再考慮下如何對(duì)一個(gè)數(shù)據(jù)進(jìn)行堆化操作纺腊。要一個(gè)一個(gè)的從數(shù)組中取出數(shù)據(jù)來建立堆吧畔咧,不用!先看一個(gè)數(shù)組揖膜,如下圖:



很明顯誓沸,對(duì)葉子結(jié)點(diǎn)來說,可以認(rèn)為它已經(jīng)是一個(gè)合法的堆了即20壹粟,60拜隧, 65, 4煮寡, 49都分別是一個(gè)合法的堆虹蓄。只要從A[4]=50開始向下調(diào)整就可以了。然后再取A[3]=30幸撕,A[2] = 17薇组,A[1] = 12,A[0] = 9分別作一次向下調(diào)整操作就可以了坐儿。下圖展示了這些步驟:



寫出堆化數(shù)組的代碼:
//建立最小堆  
void MakeMinHeap(int a[], int n)  
{  
    for (int i = n / 2 - 1; i >= 0; i--)  
        MinHeapFixdown(a, i, n);  
}  

堆排序

首先可以看到堆建好之后堆中第0個(gè)數(shù)據(jù)是堆中最小的數(shù)據(jù)律胀。取出這個(gè)數(shù)據(jù)再執(zhí)行下堆的刪除操作。這樣堆中第0個(gè)數(shù)據(jù)又是堆中最小的數(shù)據(jù)貌矿,重復(fù)上述步驟直至堆中只有一個(gè)數(shù)據(jù)時(shí)就直接取出這個(gè)數(shù)據(jù)炭菌。
由于堆也是用數(shù)組模擬的,故堆化數(shù)組后逛漫,第一次將A[0]與A[n - 1]交換黑低,再對(duì)A[0…n-2]重新恢復(fù)堆。第二次將A[0]與A[n – 2]交換酌毡,再對(duì)A[0…n - 3]重新恢復(fù)堆克握,重復(fù)這樣的操作直到A[0]與A[1]交換。由于每次都是將最小的數(shù)據(jù)并入到后面的有序區(qū)間枷踏,故操作完成后整個(gè)數(shù)組就有序了菩暗。有點(diǎn)類似于直接選擇排序

void MinheapsortTodescendarray(int a[], int n)  
{  
    for (int i = n - 1; i >= 1; i--)  
    {  
        Swap(a[i], a[0]);  
        MinHeapFixdown(a, 0, i);  
    }  
}  

注意使用最小堆排序后是遞減數(shù)組,要得到遞增數(shù)組旭蠕,可以使用最大堆停团。
由于每次重新恢復(fù)堆的時(shí)間復(fù)雜度為O(logN)旷坦,共N - 1次重新恢復(fù)堆操作,再加上前面建立堆時(shí)N / 2次向下調(diào)整佑稠,每次調(diào)整時(shí)間復(fù)雜度也為O(logN)秒梅。二次操作時(shí)間相加還是O(N * logN)。故堆排序的時(shí)間復(fù)雜度為O(N * logN)讶坯。

轉(zhuǎn)載請(qǐng)標(biāo)明出處番电,原文地址:http://blog.csdn.net/morewindows/article/details/6709644

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辆琅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌这刷,老刑警劉巖婉烟,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異暇屋,居然都是意外死亡似袁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門咐刨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昙衅,“玉大人,你說我怎么就攤上這事定鸟《妫” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵联予,是天一觀的道長啼县。 經(jīng)常有香客問我,道長沸久,這世上最難降的妖魔是什么季眷? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮卷胯,結(jié)果婚禮上子刮,老公的妹妹穿的比我還像新娘。我一直安慰自己窑睁,他們只是感情好挺峡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卵慰,像睡著了一般沙郭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裳朋,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天病线,我揣著相機(jī)與錄音吓著,去河邊找鬼。 笑死送挑,一個(gè)胖子當(dāng)著我的面吹牛绑莺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惕耕,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼纺裁,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了司澎?” 一聲冷哼從身側(cè)響起欺缘,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挤安,沒想到半個(gè)月后谚殊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛤铜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年嫩絮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片围肥。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剿干,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出穆刻,到底是詐尸還是另有隱情置尔,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布蛹批,位于F島的核電站撰洗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏腐芍。R本人自食惡果不足惜差导,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望猪勇。 院中可真熱鬧设褐,春花似錦、人聲如沸泣刹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椅您。三九已至外冀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掀泳,已是汗流浹背雪隧。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來泰國打工西轩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脑沿。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓藕畔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庄拇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子注服,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

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