堆排序

堆排序

1.前提知識

堆:(英語:heap)是計算機科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱胡本。堆通常是一個可以被看做一棵樹的數(shù)組對象铲掐。

  • 堆中某個節(jié)點的值總是不大于或不小于其父節(jié)點的值渣蜗;

  • 堆總是一棵完全二叉樹焙畔。

將根節(jié)點最大的堆叫做最大堆或大根堆,根節(jié)點最小的堆叫做最小堆或小根堆会涎。

優(yōu)先隊列(priority queue)

普通的隊列是一種先進先出的數(shù)據(jù)結(jié)構(gòu),元素在隊列尾追加瑞凑,而從隊列頭刪除末秃。在優(yōu)先隊列中,元素被賦予優(yōu)先級籽御。當(dāng)訪問元素時练慕,具有最高優(yōu)先級的元素最先刪除惰匙。優(yōu)先隊列具有最高級先出 (first in, largest out)的行為特征。通常采用堆數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)铃将。

a.完全二叉樹

若設(shè)二叉樹的深度為h项鬼,除第 h 層外,其它各層 (1~h-1) 的結(jié)點數(shù)都達到最大個數(shù)劲阎,第 h 層所有的結(jié)點都連續(xù)集中在最左邊绘盟,這就是完全二叉樹。

完全二叉樹是由滿二叉樹(一個二叉樹悯仙,如果每一個層的結(jié)點數(shù)都達到最大值龄毡,則這個二叉樹就是滿二叉樹。也就是說锡垄,如果一個二叉樹的層數(shù)為K沦零,且結(jié)點總數(shù)是(2^k) -1 ,則它就是滿二叉樹偎捎。)而引出來的蠢终。對于深度為K的,有n個結(jié)點的二叉樹茴她,當(dāng)且僅當(dāng)其每一個結(jié)點都與深度為K的滿二叉樹中編號從1至n的結(jié)點一一對應(yīng)時稱之為完全二叉樹寻拂。

(1)所有的葉結(jié)點都出現(xiàn)在第k層或k-l層(層次最大的兩層)

(2)對任一結(jié)點,如果其右子樹的最大層次為L丈牢,則其左子樹的最大層次為L或L+l祭钉。

一棵二叉樹至多只有最下面的兩層上的結(jié)點的度數(shù)可以小于2,并且最下層上的結(jié)點都集中在該層最左邊的若干位置上己沛,則此二叉樹成為完全二叉樹慌核,并且最下層上的結(jié)點都集中在該層最左邊的若干位置上,而在最后一層上申尼,右邊的若干結(jié)點缺失的二叉樹垮卓,則此二叉樹成為完全二叉樹。

b.小根堆和大根堆

假設(shè)有一棵完全二叉樹师幕,在滿足作為完全二叉樹的基礎(chǔ)上粟按,對于任意一個擁有父節(jié)點的子節(jié)點,其數(shù)值均不小于父節(jié)點的值霹粥;這樣層層遞推灭将,就是根節(jié)點的值最小,這樣的樹后控,稱為小根堆庙曙。

同理,又有一棵完全二叉樹浩淘,對于任意一個子節(jié)點來說捌朴,均不大于其父節(jié)點的值吴攒,如此遞推,就是根節(jié)點的值是最大的男旗,這樣的數(shù)舶斧,稱為大根堆。

2.一些規(guī)律

最后一個非葉節(jié)點的位置為nums.length-1察皇,所有擁有左右子節(jié)點的節(jié)點與子節(jié)點的位置關(guān)系為茴厉,假設(shè)子節(jié)點的編號為n,那么左子節(jié)點的編號為2n+1右節(jié)點的編號為2(n+1)

然后具體的做法為1.先建立大(惺踩佟)頂堆矾缓,2.交換堆頂?shù)奈恢煤驼{(diào)整整個堆,每調(diào)整一次堆的長度就減少一稻爬,直到堆的大小為0或1結(jié)束

3.先實現(xiàn)大頂堆

public static void main(String[] args) {
        int[] array = new int[]{1,3,4,2,5};
        heapsort(array);

其結(jié)構(gòu)就相當(dāng)于


image.png

然后我們先實現(xiàn)大頂堆

  private static int[] heapsort(int[] array) {
        buildBigheap(array);
    }

從最后一個非葉節(jié)點的節(jié)點開始調(diào)整嗜闻,一直調(diào)整到根節(jié)點,這樣大頂堆就建立好了

private static void buildBigheap(int[] array) {
        for(int i=array.length/2-1;i>-1;i--){ //從最后一個非葉節(jié)點的節(jié)點開始調(diào)整桅锄,一直調(diào)整到根節(jié)點
            adjustheap(array,i,array.length);  
        }

然后是具體的調(diào)整過程:

private static void adjustheap(int[] array, int i,int end) {
        //如果沒有左右子樹或者左右子樹現(xiàn)在已經(jīng)是拍好序的順序
        if(2*i+1 >=end){
            return;
        }else if(2*(i+1)>=end){
            if(array[2*i+1]>=array[i]){
                swap(array,2*i+1,i);
            }
        }else{
            //根已為最大
            if(array[i]>=array[2*i+1]&&array[i]>=array[2*(i+1)]){
                return;
                //左子樹最大,交換后去排列左邊
            }else if(array[2*i+1]>=array[i]&&array[2*i+1]>=array[2*(i+1)]){
                swap(array,2*i+1,i);
                adjustheap(array,2*i+1,end);
                //右子樹最大琉雳,交換后去排列右邊
            }else{
                swap(array,2*(i+1),i);
                adjustheap(array,2*(i+1),end);
            }
        }
    }

**具體就是先判斷現(xiàn)在調(diào)整的位置,
i(位置) \begin{cases} A.如果現(xiàn)在所在的節(jié)點已經(jīng)沒有子節(jié)點友瘤,那么直接返回\\ B.如果現(xiàn)在只有一個左子節(jié)點翠肘,那么與根節(jié)點直接比較后返回\\ C.雙子節(jié)點都在,那么就分為3種情況 \end{cases}
A狀況說明(分成兩種情況:1.該節(jié)點是葉節(jié)點辫秧,2束倍,該節(jié)點往后的子節(jié)點已經(jīng)被拍好序了)
B狀況說明(因為當(dāng)一個節(jié)點只含有左子節(jié)點可以用的時候,那么他的右節(jié)點一定是空或者已經(jīng)排好序了)
C狀況說明(如果根最大什么都不用做盟戏,直接返回绪妹,如果左子節(jié)點最大,那么與根交換柿究,并對左子節(jié)點的剩余節(jié)點進行調(diào)整邮旷,同理可得右子節(jié)點)
OK
現(xiàn)在做完這一步我們可以看一下數(shù)組結(jié)構(gòu)

image.png

image.png

這個時候的根是最大的也就是已經(jīng)實現(xiàn)了大頂堆

4.交換堆頂元素與最后一個元素,實現(xiàn)排序

其實這一過程就是交換調(diào)整蝇摸,每一次交換都會使堆的需排序的大小減一廊移,那么很容易就想到當(dāng)堆大小為1時,調(diào)整完畢探入,所以用一個for循環(huán)for i array.length ->1

 for(int i =array.length-1;i>0;i--){
            swap(array,i,0);  //交換棧頂元素與堆的最后一個數(shù)據(jù)(每次最后一個數(shù)據(jù)都會減一)
            adjustheap(array,0,i);  //調(diào)整堆為大頂堆
        }

例如第一次交換后的數(shù)據(jù)為


image.png

然后調(diào)整完的數(shù)據(jù)為:


image.png

這樣做完后所有的序都已經(jīng)排好了

完整代碼

public static void main(String[] args) {
        int[] array = new int[]{1,3,4,2,5};
        heapsort(array);
        for(int i=0;i<array.length;i++){
            System.out.printf(array[i]+" ");
        }
    }

    private static int[] heapsort(int[] array) {
        buildBigheap(array);
        return array;
    }

    private static void buildBigheap(int[] array) {
        for(int i=array.length/2-1;i>-1;i--){
            adjustheap(array,i,array.length);
        }

        for(int i =array.length-1;i>0;i--){
            swap(array,i,0);
            adjustheap(array,0,i);
        }
    }
    private static void adjustheap(int[] array, int i,int end) {
        //如果沒有左右子樹或者左右子樹現(xiàn)在已經(jīng)是拍好序的順序
        if(2*i+1 >=end){
            return;
        }else if(2*(i+1)>=end){
            if(array[2*i+1]>=array[i]){
                swap(array,2*i+1,i);
            }
        }else{
            //根已為最大
            if(array[i]>=array[2*i+1]&&array[i]>=array[2*(i+1)]){
                return;
                //左子樹最大,交換后去排列左邊
            }else if(array[2*i+1]>=array[i]&&array[2*i+1]>=array[2*(i+1)]){
                swap(array,2*i+1,i);
                adjustheap(array,2*i+1,end);
                //右子樹最大,交換后去排列右邊
            }else{
                swap(array,2*(i+1),i);
                adjustheap(array,2*(i+1),end);
            }
        }
    }

    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] =array[j];
        array[j] = temp;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懂诗,一起剝皮案震驚了整個濱河市蜂嗽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌殃恒,老刑警劉巖植旧,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辱揭,死亡現(xiàn)場離奇詭異,居然都是意外死亡病附,警方通過查閱死者的電腦和手機问窃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來完沪,“玉大人域庇,你說我怎么就攤上這事「不” “怎么了听皿?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宽档。 經(jīng)常有香客問我尉姨,道長,這世上最難降的妖魔是什么吗冤? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任又厉,我火速辦了婚禮,結(jié)果婚禮上椎瘟,老公的妹妹穿的比我還像新娘覆致。我一直安慰自己,他們只是感情好降传,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布篷朵。 她就那樣靜靜地躺著,像睡著了一般婆排。 火紅的嫁衣襯著肌膚如雪声旺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天段只,我揣著相機與錄音腮猖,去河邊找鬼。 笑死赞枕,一個胖子當(dāng)著我的面吹牛澈缺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炕婶,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼姐赡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了柠掂?” 一聲冷哼從身側(cè)響起项滑,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涯贞,沒想到半個月后枪狂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體危喉,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年州疾,在試婚紗的時候發(fā)現(xiàn)自己被綠了辜限。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡严蓖,死狀恐怖薄嫡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谈飒,我是刑警寧澤岂座,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站杭措,受9級特大地震影響费什,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜手素,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一鸳址、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泉懦,春花似錦稿黍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邓嘹,卻和暖如春酣栈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汹押。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工矿筝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棚贾。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓窖维,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妙痹。 傳聞我的和親對象是個殘疾皇子铸史,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

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

  • 關(guān)于最大堆 什么是最大堆和最小堆?最大(欣场)堆是指在樹中,存在一個結(jié)點而且該結(jié)點有兒子結(jié)點猩系,該結(jié)點的data域值都...
    凌云壯志幾多愁閱讀 88,119評論 33 71
  • 二叉樹 滿二叉樹 國內(nèi)教程定義:一個二叉樹媚送,如果每一個層的結(jié)點數(shù)都達到最大值,則這個二叉樹就是滿二叉樹寇甸。也就是說塘偎,...
    簡_愛SimpleLove閱讀 4,279評論 0 3
  • 本文的目標(biāo)是要做出優(yōu)先隊列和堆排序兩個Demo。 完全二叉樹 優(yōu)先隊列 堆排序 完全二叉樹 完全二叉樹的定義是建立...
    囧書閱讀 4,966評論 13 48
  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系拿霉,并對這種結(jié)構(gòu)定義相應(yīng)的運算吟秩,而且確保經(jīng)過這...
    Winterfell_Z閱讀 5,854評論 0 13
  • 堆排序可以做什么 首先應(yīng)該弄清楚堆排序可以解決什么問題,答案是顯而易見的:排序绽淘。說得通俗點兒就是對一組無序的數(shù)字進...
    Springlord888閱讀 30,368評論 11 52