1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        卷起來,淺析堆排序算法!

        共 2449字,需瀏覽 5分鐘

         ·

        2021-11-27 02:38


        堆這種數(shù)據(jù)結(jié)構(gòu)應(yīng)用場景很多,最經(jīng)典的莫過于堆排序。堆排序(Heap Sort)是一種原地的、時(shí)間復(fù)雜度為O(nlogn)的排序算法。我們今天就來分析一下堆排序。

        一、算法描述

        堆排序按照從小到大的順序進(jìn)行排序的步驟如下:

        1. 將長度為 n?的序列構(gòu)造成為一個(gè)大頂堆;

        2.?將根結(jié)點(diǎn)與序列最后一個(gè)結(jié)點(diǎn)進(jìn)行交換,此時(shí)最后一個(gè)結(jié)點(diǎn)就是該序列的最大值;

        3. 將剩余的 n - 1 個(gè)結(jié)點(diǎn)再次構(gòu)造成為一個(gè)大頂堆;

        4. 重復(fù)步驟 2、3,直到構(gòu)造成一個(gè)完全有序的序列。

        堆需要滿足兩個(gè)條件:

        1. 是一顆完全二叉樹(Complete Binary Tree)


        2. 堆上面的每一個(gè)節(jié)點(diǎn)都必須滿足父結(jié)點(diǎn)的值大于等于子結(jié)點(diǎn)的值或者父結(jié)點(diǎn)的值小于等于子結(jié)點(diǎn)的值 。parent >= children or parent <= children

        二、完全二叉樹

        一棵深度為 k 的有 n 個(gè)結(jié)點(diǎn)的二叉樹,對樹中的結(jié)點(diǎn)按從上至下、從左到右的順序進(jìn)行編號(hào),如果編號(hào)為 i(1≤i≤n)的結(jié)點(diǎn)與滿二叉樹中編號(hào)為 i 的結(jié)點(diǎn)在二叉樹中的位置相同,則這棵二叉樹稱為完全二叉樹。

        舉個(gè)栗子:

        滿二叉樹:

        深度為 k 且有 2^k-1 個(gè)結(jié)點(diǎn)的二叉樹稱為滿二叉樹。


        完全二叉樹:

        樹中含有 n 個(gè)結(jié)點(diǎn)和滿二叉樹中編號(hào)為 1 至 n 的結(jié)點(diǎn)一一對應(yīng)。


        三、大頂堆和小頂堆

        堆又分為大頂堆和小頂堆。

        大頂堆:

        完全二叉樹且每個(gè)父節(jié)點(diǎn)都大于等于它的兩個(gè)子結(jié)點(diǎn)時(shí),就稱為大頂堆

        公式定義:

        arr[i] >= arr[2i+1]?
        arr[i] >= arr[2i+2]??

        小頂堆:

        完全二叉樹每個(gè)父節(jié)點(diǎn)都小于等于它的兩個(gè)子結(jié)點(diǎn)時(shí),就稱為小頂堆

        公式定義:

        arr[i] <= arr[2i+1]
        arr[i] <= arr[2i+2]??


        四、堆的性質(zhì)

        例如我們已經(jīng)有一個(gè)大頂堆,那么這個(gè)大頂堆具有哪些特征呢?



        將其映射為數(shù)組:


        int arr[] = {12,10,9,6,7,8,5,3,2}



        我們假設(shè)結(jié)點(diǎn) 6 的位置為 i = 3。那么我們可以求出,他的父節(jié)點(diǎn)和兩個(gè)子結(jié)點(diǎn)。


        Parent =?( i - 1 )? /? 2,等于位置為 1 的結(jié)點(diǎn) 10。


        C1 = 2i + 1,等于位置為 7 的結(jié)點(diǎn) 3 。


        C2 = 2i + 2,等于位置為 8 的結(jié)點(diǎn) 2 。

        五、圖解堆排序算法

        我們以 int tree[] = {6, 10, 3, 8, 5, 12, 7, 2, 9} 為例,對堆排序的執(zhí)行過程進(jìn)行圖解。


        第一步:通過原始序列構(gòu)造一個(gè)大頂堆(升序采用大頂堆,降序采用小頂堆)。


        1. 先通過序列通過從上到下從左往右的順序構(gòu)造一個(gè)完全二叉樹



        2.? 對第三層的父節(jié)點(diǎn)和第四層的子結(jié)點(diǎn)進(jìn)行調(diào)整,其中粉紅色代表發(fā)生交換的結(jié)點(diǎn)。使得其父節(jié)點(diǎn)大于兩個(gè)子結(jié)點(diǎn)。


        即也是從最后 1 個(gè)非葉子結(jié)點(diǎn)開始 (8 - 1) / 2 = 3, 也就是從 8 開始進(jìn)行從左到右從下到上進(jìn)行調(diào)整。


        [8,2,9]?中,9 最大,交換 8 和 9。



        3. 對第二層父節(jié)點(diǎn)和第三層子結(jié)點(diǎn)進(jìn)行調(diào)整。


        [10,9,5]?中,10?最大,不交換。


        [3,12,7]?中,12 最大,交換 3 和 12。



        4. 對第一層父節(jié)點(diǎn)和第二層子結(jié)點(diǎn)進(jìn)行調(diào)整。


        [6,10,12]?中,12 最大,交換 6 和 12。



        這時(shí),上面操作導(dǎo)致?[6,3,7]?不是一個(gè)大頂堆,繼續(xù)調(diào)整。


        [6,3,7]?中,7 最大,交換 6 和 7。



        至此,大頂堆構(gòu)建完成。


        第二步:將根結(jié)點(diǎn)調(diào)整到最后一個(gè)位置,使末尾元素最大。然后對剩下元素繼續(xù)調(diào)整堆,根結(jié)點(diǎn)調(diào)整到最后一個(gè)位置,得到第二大元素。如此反復(fù)進(jìn)行交換、重夠、交換。


        1. 將根節(jié)點(diǎn)元素 12 跟 8 交換。



        2. 重新調(diào)整結(jié)構(gòu),使其繼續(xù)滿足堆定義。



        3. 將根節(jié)點(diǎn)元素 10 跟 2 交換。



        4. 重新調(diào)整結(jié)構(gòu),使其繼續(xù)滿足堆定義。



        這樣一直交換、重夠、交換下去.......


        5.最后一次將根節(jié)點(diǎn)元素 3 跟 2 交換。



        這樣所有元素就達(dá)到了有序狀態(tài)。


        六、算法實(shí)現(xiàn)

        第一步:構(gòu)建大頂堆


        #include?

        void?swap(int?arr[], int?i, int?j){
        ??int?temp = arr[i];
        ??arr[i] = arr[j];
        ??arr[j] = temp;
        }

        void?heapify(int?tree[], int?n, int?i){
        ??if?(i >= n){
        ????return;
        ??}
        ??
        ??int?c1 = 2?* i + 1;
        ??int?c2 = 2?* i + 2;
        ??int?max = i;
        ??if?(c1 < n && tree[c1] > tree[max]){
        ????max = c1;
        ??}
        ??if?(c2 < n && tree[c2] > tree[max]){
        ????max = c2;
        ??}
        ??if?(max != i){
        ????swap(tree, max ,i);
        ????heapify(tree, n, max);
        ??}
        }

        void?build_heap(int?tree[], int?n){
        ??int?last_node = n - 1;
        ??int?parent = (last_node - 1) / 2;
        ??for?(int?i = parent; i >= 0; i--){
        ????heapify(tree, n, i);
        ??}
        }

        void?heap_sort(int?tree[], int?n){
        ??build_heap(tree, n);
        ??for?(int?i = n - 1; i >= 0; i--){
        ????swap(tree, i, 0);
        ????heapify(tree, i, 0);
        ??}
        }

        int?main(){
        ??int?tree[] = {6, 10, 3, 9, 5, 12, 7, 2, 8};
        ??int?n = 9;
        ??
        ??build_heap(tree, 9);
        // heap_sort(tree, 9);
        ??
        ??for(int?i = 0; i < n; i++){
        ????printf("%d\n",tree[i]);
        ??}
        ??return?0;
        }


        輸出:



        我們將它畫出來康康:



        看是不是一顆大頂堆?那必須是呀。

        第二步:排序


        #include?

        void?swap(int?arr[], int?i, int?j){
        ??int?temp = arr[i];
        ??arr[i] = arr[j];
        ??arr[j] = temp;
        }

        void?heapify(int?tree[], int?n, int?i){
        ??if?(i >= n){
        ????return;
        ??}
        ??
        ??int?c1 = 2?* i + 1;
        ??int?c2 = 2?* i + 2;
        ??int?max = i;
        ??if?(c1 < n && tree[c1] > tree[max]){
        ????max = c1;
        ??}
        ??if?(c2 < n && tree[c2] > tree[max]){
        ????max = c2;
        ??}
        ??if?(max != i){
        ????swap(tree, max ,i);
        ????heapify(tree, n, max);
        ??}
        }

        void?build_heap(int?tree[], int?n){
        ??int?last_node = n - 1;
        ??int?parent = (last_node - 1) / 2;
        ??for?(int?i = parent; i >= 0; i--){
        ????heapify(tree, n, i);
        ??}
        }

        void?heap_sort(int?tree[], int?n){
        ??build_heap(tree, n);
        ??for?(int?i = n - 1; i >= 0; i--){
        ????swap(tree, i, 0);
        ????heapify(tree, i, 0);
        ??}
        }

        int?main(){
        ??int?tree[] = {6, 10, 3, 9, 5, 12, 7, 2, 8};
        ??int?n = 9;
        ??
        // build_heap(tree, 9);
        ??heap_sort(tree, 9);
        ??
        ??for(int?i = 0; i < n; i++){
        ????printf("%d\n",tree[i]);
        ??}
        ??return?0;
        }


        輸出:


        我們將它畫出來康康:



        就這樣我們就完成堆排序啦!開不開森。


        六、算法分析

        時(shí)間復(fù)雜度堆排序包括建堆和排序兩個(gè)操作,建堆過程的時(shí)間復(fù)雜度是 O(n),排序過程的時(shí)間復(fù)雜度是 O(nlogn),所以,堆排序整體的時(shí)間復(fù)雜度是 O(nlogn)。


        空間復(fù)雜度?因?yàn)槎雅判蚴蔷偷嘏判?,空間復(fù)雜度為常數(shù):O(1)


        穩(wěn)定性:不穩(wěn)定。因?yàn)樵诙训恼{(diào)整過程中,元素進(jìn)行比較和交換所走的是該結(jié)點(diǎn)到葉子結(jié)點(diǎn)的一條路徑,因此對于相同的元素就可能出現(xiàn)排在后面的元素被交換到前面來的情況。?

        七、適用場景

        堆排序在建立堆和調(diào)整堆的過程中會(huì)產(chǎn)生比較大的開銷,在元素少的時(shí)候并不適用。但是,在元素比較多的情況下,還是不錯(cuò)的一個(gè)選擇。尤其是在解決諸如“前n大的數(shù)”一類問題時(shí),幾乎是首選算法。


        如果覺得寫的不錯(cuò),點(diǎn)贊?+ 在看來一個(gè)
        瀏覽 41
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲日韩电影在线观看 | 蜜桃91精品入口 | 影音先锋亚洲无码 | 台湾精品一区二区三区 | 国产成人免费在线观看 | 欧美大尺寸suv视频 | 海角国产乱辈乱精品视频 | 高清无码在线免费视频 | 啊灬啊灬啊灬啊灬高潮了在线 | 东方av在 |