選擇排序:堆排序(Heap Sort)

堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進癣亚。

基本思想:

堆頂元素(即第一個元素)必為最小項(小頂堆)或最大項(大頂堆)颤霎。若以一維數(shù)組存儲一個堆,則堆對應(yīng)一棵完全二叉樹党饮,且所有非葉結(jié)點的值均不大于(或不小于)其子女的值摩泪,根結(jié)點(堆頂元素)的值是最小(或最大)的。

初始時把要排序的n個數(shù)的序列看作是一棵順序存儲的二叉樹(一維數(shù)組存儲二叉樹)劫谅,調(diào)整它們的存儲序见坑,使之成為一個堆,將堆頂元素輸出捏检,得到n個元素中最大(或最小)的元素荞驴,這時堆的根節(jié)點的數(shù)最大(或最小)贯城。然后對前面(n-1)個元素重新調(diào)整使之成為堆熊楼,輸出堆頂元素,得到n個元素中次大(或次小)的元素能犯。依此類推鲫骗,直到只有兩個節(jié)點的堆,并對它們作交換踩晶,最后得到有n個節(jié)點的有序序列执泰。稱這個過程為堆排序

因此渡蜻,實現(xiàn)堆排序需解決兩個問題:

  1. 如何將n個待排序的數(shù)建成堆术吝。

  2. 輸出堆頂元素后计济,怎樣調(diào)整剩余 n-1 個元素,使其成為一個新堆排苍。

首先討論第二個問題:

輸出堆頂元素后沦寂,對剩余 n-1 元素重新建成堆的調(diào)整過程(調(diào)整大頂堆的方法):

1)設(shè)有 n 個元素的堆,輸出堆頂元素后淘衙,剩下 n-1 個元素传藏。將堆底元素送入堆頂((最后一個元素與堆頂進行交換),堆被破壞彤守,其原因僅是根結(jié)點不滿足堆的性質(zhì)毯侦。

2)將根結(jié)點與左、右子樹中較大元素的進行交換遗增。

3)若與左子樹交換:如果左子樹堆被破壞叫惊,即左子樹的根結(jié)點不滿足堆的性質(zhì),則重復(fù)方法 (2).

4)若與右子樹交換做修,如果右子樹堆被破壞霍狰,即右子樹的根結(jié)點不滿足堆的性質(zhì)。則重復(fù)方法 (2).

5)繼續(xù)對不滿足堆性質(zhì)的子樹進行上述交換操作饰及,直到葉子結(jié)點蔗坯,堆被建成。

稱這個自根結(jié)點到葉子結(jié)點的調(diào)整過程為篩選燎含。

再討論對 n 元素初始建堆的過程宾濒。

建堆方法:對初始序列建堆的過程,就是一個反復(fù)進行篩選的過程屏箍。

1)n 個結(jié)點的完全二叉樹仓坞,則最后一個結(jié)點是第 n/2 個結(jié)點的子樹捶枢。

2)篩選從第 n/2 個結(jié)點為根的子樹開始实幕,該子樹成為堆空厌。

3)之后向前依次對各結(jié)點為根的子樹進行篩選,使之成為堆颖御,直到根結(jié)點榄棵。

算法的實現(xiàn):

從算法描述來看,堆排序需要兩個過程潘拱,一是建立堆疹鳄,二是堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數(shù)組成芦岂。一是建堆的滲透函數(shù)瘪弓,二是反復(fù)調(diào)用滲透函數(shù)實現(xiàn)排序的函數(shù)。

// 輸出數(shù)組內(nèi)容
void print(int array[], int length, int i) {
    printf("i=%d:",i);
    for (int j = 0; j < length; j++) {
        printf(" %d ", array[j]);
    }
    printf("\n");
}

/**
 * 已知 H[s...m] 除了 H[s] 外均滿足堆的定義
 * 調(diào)整 H[s] 使其成為大頂堆盔腔,即將對第 s 個結(jié)點為根的子樹篩選
 *
 * @param H 是待調(diào)整的堆數(shù)組
 * @param s 是待調(diào)整的數(shù)組元素的位置
 * @param length 是數(shù)組的長度
 */
void HeapAdjust(int H[], int s, int length) {
    int temp = H[s];
    int child = 2*s + 1; // 左孩子結(jié)點的位置
    
    while (child < length) {
        if (child + 1 < length && H[child] < H[child+1]) {
            // 如果右孩子大于左孩子(找到比當(dāng)前待調(diào)整結(jié)點大的孩子結(jié)點)
            ++ child;
        }
        if (H[s] < H[child]) { // 如果較大的子結(jié)點大于父結(jié)點
            H[s] = H[child]; // 那么把較大的子結(jié)點往上移杠茬,替換它的父結(jié)點
            s = child; // 重新設(shè)置 s月褥,即待調(diào)整的下一個結(jié)點的位置
            child = 2*s + 1;
        } else {
            // 如果當(dāng)前待調(diào)整結(jié)點大于它的左右孩子弛随,則不需要調(diào)整瓢喉,直接退出
            break;
        }
        H[s] = temp; // 當(dāng)前待調(diào)整的結(jié)點放到比其大的孩子結(jié)點位置上
    }
    print(H, length, s);
}

/**
 * 初始堆進行調(diào)整
 * 將 H[0...length-1]建成堆
 * 調(diào)整完之后第一個元素是序列的最小值
 */
void BuildingHeap(int H[], int length) {
    // 最后一個有孩子的節(jié)點的位置 i = (length - 1) / 2
    for (int i = (length -1) / 2; i >= 0; i --) {
        HeapAdjust(H, i, length);
    }
}

/**
 * 堆排序算法
 */
void HeapSort(int H[], int length) {
    // 初始堆
    BuildingHeap(H, length);
    
    // 從最后一個元素開始對序列進行調(diào)整
    for (int i = length - 1; i > 0; i --) {
        // 交換堆頂元素 H[0] 和堆中最后一個元素
        int temp = H[i];
        H[i] = H[0];
        H[0] = temp;
        
        // 每次交換堆頂元素和堆中最后一個元素之后,都要對堆進行調(diào)整
        HeapAdjust(H, 0, i);
    }
}

int main(int argc, const char * argv[]) {
    int arrayHeapSort[8] = {  49,38,65,97,76,13,27,49  };
    HeapSort(arrayHeapSort, 8);
    print(arrayHeapSort, 8, 0);
    printf("\n");
    
    return 0;
}

總結(jié)

堆排序最壞情況下舀透,時間復(fù)雜度也為:O(nlogn )栓票。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市愕够,隨后出現(xiàn)的幾起案子走贪,更是在濱河造成了極大的恐慌,老刑警劉巖惑芭,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坠狡,死亡現(xiàn)場離奇詭異,居然都是意外死亡遂跟,警方通過查閱死者的電腦和手機逃沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幻锁,“玉大人凯亮,你說我怎么就攤上這事『宥” “怎么了假消?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長岭接。 經(jīng)常有香客問我富拗,道長,這世上最難降的妖魔是什么鸣戴? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任啃沪,我火速辦了婚禮,結(jié)果婚禮上葵擎,老公的妹妹穿的比我還像新娘谅阿。我一直安慰自己,他們只是感情好酬滤,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布签餐。 她就那樣靜靜地躺著,像睡著了一般盯串。 火紅的嫁衣襯著肌膚如雪氯檐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天体捏,我揣著相機與錄音冠摄,去河邊找鬼糯崎。 笑死,一個胖子當(dāng)著我的面吹牛河泳,可吹牛的內(nèi)容都是我干的沃呢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼拆挥,長吁一口氣:“原來是場噩夢啊……” “哼薄霜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纸兔,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤惰瓜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汉矿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崎坊,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年洲拇,在試婚紗的時候發(fā)現(xiàn)自己被綠了奈揍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡呻待,死狀恐怖打月,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚕捉,我是刑警寧澤奏篙,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站迫淹,受9級特大地震影響秘通,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜敛熬,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一肺稀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧应民,春花似錦话原、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至归园,卻和暖如春黄虱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庸诱。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工捻浦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晤揣,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓朱灿,卻偏偏與公主長得像昧识,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子母剥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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

  • 概述:排序有內(nèi)部排序和外部排序滞诺,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序形导,而外部排序是因排序的數(shù)據(jù)很大环疼,一次不能容納全部...
    每天刷兩次牙閱讀 3,729評論 0 15
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,243評論 0 2
  • 第一章 緒論 什么是數(shù)據(jù)結(jié)構(gòu)? 數(shù)據(jù)結(jié)構(gòu)的定義:數(shù)據(jù)結(jié)構(gòu)是相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合朵耕。 第二章...
    SeanCheney閱讀 5,755評論 0 19
  • 概述排序有內(nèi)部排序和外部排序炫隶,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序,而外部排序是因排序的數(shù)據(jù)很大阎曹,一次不能容納全部的...
    Luc_閱讀 2,259評論 0 35
  • 概述 排序有內(nèi)部排序和外部排序伪阶,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序,而外部排序是因排序的數(shù)據(jù)很大处嫌,一次不能容納全部...
    蟻前閱讀 5,168評論 0 52