排序--最大堆構造和堆排序(單步查看過程)

来源:https://www.cnblogs.com/payapa/archive/2019/07/16/11192303.html
-Advertisement-
Play Games

這裡先簡單說下最大堆的基本性質: 最大堆一定是完全二叉樹 當父節點為 n 時,左孩子為 n 2 + 1,右孩子為 n 2 + 2 當孩子為 n 時,其父節點為: (n 1) / 2 這一點很重要,在後面初始化的時候會用到 父節點大於等於左孩子和右孩子,但左孩子不一定大於右孩子 瞭解以上基本性質之後, ...


這裡先簡單說下最大堆的基本性質:

  • 最大堆一定是完全二叉樹
  • 當父節點為 n 時,左孩子為 n * 2 + 1,右孩子為 n * 2 + 2
  • 當孩子為 n 時,其父節點為: (n - 1) / 2 ----> 這一點很重要,在後面初始化的時候會用到
  • 父節點大於等於左孩子和右孩子,但左孩子不一定大於右孩子

瞭解以上基本性質之後,就可以先看一下如何對一個序列做最大堆的初始化。


最大堆的構造

思路:過程就像冒泡一樣,從最序號最大的父節點開始,查看是否滿足最大堆,如果不滿足,則調整(調整之後,還要查看被調整的節點是否依然滿足最大堆性質,如果不滿足,則需要往下遍歷調整,這部分在後面的舉例中會有說明),如果滿足,則繼續查看前一個父節點是否滿足,直接最終的0節點。

例如:這裡有個數組:x[] = {2 5 3 9 7 1 6},則對應樹為:

該序列長度為7,最大下標為6,則最大的父節點下標就是: (6 - 1)/ 2 是2(基本性質第三條),對應的數值是3,
他的左孩子是1,右孩子是6,右孩子比父節點大,所以應該調整一下這兩個節點,得到:

該節點調整完之後,再查看前一個父節點,下標為1,對應的數值為5,
他的左孩子是9,右孩子是7,不滿足,所以父節點應該與左孩子進行交換,得到:

繼續往前,再前面一個父節點下標為0,數值為2,左孩子是9,右孩子是6,不滿足最大堆,父節點與左孩子交換,得到:

交換之前,左孩子為9,現在左孩子為2,導致這個左孩子不滿足最大堆性質,因為這個左孩子的左孩子大於左孩子,所以,這裡就出現了上面括弧中所說的:調整完之後,還要查看被調整的節點是否依然滿足最大堆的性質。
這裡還要對調整之後的節點繼續調整:

至此,一個最大堆就初始化完成了!

堆排序

其實,明白了最大堆怎麼構造出來的之後,堆排序就很容易了。
想一想,最大堆構造出來之後,其實就直接得到了最大值:x[0],
如果把 x[0] 與最後一個數字交換一下,然後對剩下的數字重新按之前的方法構造一下,不就找到了第二大的數字了嗎?
此時第二大的數字就是x[0],把它與剛剛參加排序的最後的一個數字交換一下,然後再對剩下的數字排序一下,就可以得到第三大的數字,
這麼一直迴圈,就可以把當前數組排序完成了。
接著剛剛的那個例題,先把9與參與排序的最後一個數字對換,得到:

此時參與排序的,就只有:3,7,6,5,2,1。
因為x[0]被調整了,所以要查看x[0]是否依然最大堆性質,顯然是不滿足的,所以繼續調整x[0],得到:

x[0]與x[1]互換之後,導致被調整的x[1]又不滿足最大堆,那就再調整一下:

現在整個樹都滿足最大堆了,也就得到了現在參與排序的最大值x[0]為7,
所以,x[0]與當前參與排序的最後一位交換,得到:

此時參與排序的,只有:1,5,6,3,2。
按照上面步驟再次迴圈,這裡就不寫了,直接放圖:

上代碼:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 列印數組
void print(int *array, int len)
{
    for (int i=0; i<len; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
}

// 交換兩個數值
void swap(int *array, int i, int j)
{
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
    return;
}

// 對當前父節點進行排序
// 查看該父節點是否滿足最大堆,如果不滿足則調整子節點
void sort (int *array, int father, int len)
{
    for (int lchild =father*2+1; lchild<len; lchild=father*2+1)
    {
        int k = lchild;  // 先用k指向左孩子
        int rchild = lchild + 1;
        if ((rchild < len) && (array[rchild] > array[lchild])) 
        {
            k = rchild;   // 如果有右孩子,且右孩子比左孩子還大,則更新k
        }

        // 這裡的k,指向了左右孩子中較大的那個
        if (array[k] > array[father])
        {
            swap(array, k, father); // 交換父親和孩子的數值

            father = k;  // 這裡就是查看被調整之後的節點k,是否依然滿足最大堆
        }
        else
        {
            break;  // 當前節點不需要被調整
        }
    }
    return;
}

int main(void)
{
    int x[] = {2,5,3,9,7,1,6};
    int len = sizeof(x)/sizeof(int);

    print(x, len); // 先輸出原始序列

    // 最大子節點下標為len-1,所以它的父節點是 (len-1-1) / 2
    for (int i = (len - 2)/2; i>=0; i--)
    {
        sort(x, i, len);
    }

    print(x, len); // 輸出初始化之後的最大堆

    for (int i=(len-1); i>0; i--)
    {
        swap(x, 0, i);  // 把最大的一個值放到末尾,然後對剩餘的數組進行排序
        sort(x, 0, i);
    }
    print(x, len); // 輸出排序之後的序列
    return 0;
}

最終輸出為:
2 5 3 9 7 1 6 
9 7 6 5 2 1 3 
1 2 3 5 6 7 9 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Spring Cloud Alibaba | 序言 @[TOC] 1. Spring Cloud Alibaba是什麼? Spring Cloud Alibaba 致力於提供微服務開發的一站式解決方案。此項目包含開發分散式應用微服務的必需組件,方便開發者通過 Spring Cloud 編程模型輕鬆使 ...
  • 我們分析軟體項目開發過程中各階段的時間占比可以慢慢體會到,一套軟體項目 UI及基本業務的設計會占到整個項目開發的50%以上的時間,再看UI設計,通用的部分占到70%以上,我們會想,如果能將這些重覆簡易的工作能否通過簡易的操作來快速實現呢? 這樣可以將大部分的時間用到後續的真實業務邏輯處理上去,就可以 ...
  • 接著學習設計模式系列,今天講解的是代理模式。 定義 什麼是代理模式? 代理模式,也叫委托模式,其定義是給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。它包含了三個角色: Subject:抽象主題角色。可以是抽象類也可以是介面,是一個最普通的業務類型定義。 RealSubject:具體主 ...
  • 《Head First設計模式》已經讀了不止一遍,但是始終沒有進行系統的進行總結。所以近期開始總結設計模式相關的知識,從模板方法模式開始,因為是一個我認為是最簡單的設計模式。(推薦視頻資源 "23個設計模式" ) 提出&解決問題 提出問題 實現製作咖啡功能 。且製作咖啡需要四個步驟 : 1. 燒水 ...
  • 舉個慄子 問題描述 小時候數學老師的隨堂測驗,都是在黑板上抄題目,然後再作答案。寫一個抄題目的程式。 簡單實現 學生甲抄的試卷 學生乙抄的試卷 測試 測試結果 存在問題 兩個學生抄試卷都非常類似,除了答案不同,沒什麼不一樣,這樣又容易錯,又難以維護。因此老師出一份試卷,列印多份,讓學生填寫答案就可以 ...
  • 架構雜談《三》 一致性問題 前面的《架構雜談一》和《架構雜談二》 雜談了從服務化到微服務架構的演進,並肯定了服務化和微服務架構是一脈相承的。微服務在服務化架構的基礎上,對服務化的細節和方案進行了優化和細化,重點突出了無中心化管理的微服務架構,通過對服務進行有效的拆分來實現敏捷開發和自動化部署,併在海 ...
  • SpringCloud系列教程 | 第十四篇:Spring Cloud Gateway高級應用 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如無特殊說明,本系列教程全採用以上版本 上一篇我們聊了Gateway和註冊中心的使用,以及 Gat ...
  • 在目前的工作中,我對Java中的Stream和Lambda表達式都使用得很多,之前也寫了兩篇文章來總結對應的知識。 "024:Java流實現Shell:cat 1.log | grep a | sort | uniq c | sort rn" "函數式編程讓你忘記設計模式" 不過對於Optional ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...