【排序算法】④堆排序

系列文章目錄

?第一篇:【排序算法】①直接插入排序-CSDN博客

第二篇:【排序算法】②希爾排序-CSDN博客

第三篇:【排序算法】③直接選擇排序-CSDN博客

第四篇:【排序算法】④堆排序-CSDN博客

第五篇:【排序算法】⑤冒泡排序-CSDN博客

第六篇:【排序算法】⑥快速排序:Hoare、挖坑法、前后指針法-CSDN博客

第七篇:【排序算法】⑦歸并排序-CSDN博客


目錄

系列文章目錄

前言

一、堆是什么?

二、實現堆排序

1.堆排序所需要的堆函數

HeapCreate:堆的構建

核心算法BigADjustUp:(大堆)向上調整算法

HeapPopBig:刪除節點

核心算法BigADjustDown:向下調整算法

HeapDestory:堆的銷毀

2.堆排序代碼

三、分析堆排序

總結



前言

堆排序是指利用二叉樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。

值得注意的是:排升序要建大堆,排降序建小堆。


一、堆是什么?

想要了解堆是什么,首先需要明白什么是完全二叉樹

完全二叉樹:

1. 二叉樹:由一個根節點加上兩棵別稱為左子樹和右子樹的二叉樹組成。

2. 滿二叉樹:一個二叉樹,如果每一個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。
3. 完全二叉樹:完全二叉樹是效率很高的數據結構,完全二叉樹是由滿二叉樹而引出來的。對于深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。 要注意的是滿二叉樹是一種特殊的完全二叉樹。

堆的定義:

如果有一個碼的集合K = {k0 ,k1 ,k2 ,…,kn-1 },把它的所有元素按完全二叉樹的順序存儲方式存儲在一個一維數組中,并滿足:ki <= k2i+1且ki <= k2i+2( ki>=k2i+1 且ki >=k2i+2 ) i = 0,1,2…,則稱為小堆(或大堆).

簡單來說:

完全二叉樹由于其性質非常適合使用順序結構存儲,所以,現實中我們通常把堆使用順序結構的數組來存儲。

于是我們可以推出堆的數據結構:

typedef int HPDataType;typedef struct Heap
{HPDataType* _a;int _size;int _capacity;
}Heap;

上面是簡述堆的概念,目的是為堆排序準備。若是之前未接觸過堆這種數據結構的讀者,強烈建議先看看鏈接文章:【數據結構】二叉樹①-堆-CSDN博客


二、實現堆排序

本文以外部建堆實現堆排升序為例進行介紹,至于本地建堆或者堆排降序由于核心思想未變,可同理得出。

1.堆排序所需要的堆函數


HeapCreate:堆的構建

通過傳入數組a和數據個數nums,直接將傳入的數組構造成小堆。

// 堆的初始化,并構建傳入數組:為堆排序
void HeapCreateBig(Heap* hp, HPDataType* a, int nums)
{if(!hp||!a)return;HPDataType* temp = (HPDataType*)malloc(sizeof(HPDataType) * nums);if (temp){hp->_a = temp;hp->_capacity = nums;hp->_size = nums;}else{perror("HeapCreate malloc fail");exit(-1);}for (int i = 0; i < nums; ++i){hp->_a[i] = a[i];BigADjustUp(hp, i);}
}

上述代碼的核心在于最后的for循環:

①在循環中,將外部a數組的數據一個一個賦予給malloc出來堆中的數組;

②每賦予一個值,便調用依次BigADjustUp函數。

核心算法BigADjustUp:(大堆)向上調整算法


假設有一個數組,在邏輯上看做一顆完全二叉樹。我們通過從根節點開始的向上調整算法可以把它調整成一個大堆。

假設該數組為:

int a[] = { 17,15,28,22,13,5,9 };

可以看到該數組中的內容并不符合大堆要求。

向上調整算法核心思路:

①默認數組第一個元素為根節點數據;

②之后每Push進一個數據,便與它的父節點比較,若父節點數據小于子節點數據,則交換,然后將父節點(下標)賦值給子節點,再計算新父節點,重復上述步驟,直至子節點到根結點處(下標為0時)。

③中途若父節點數據大于等于子節點數據(滿足小堆要求),則break跳出循環。

代碼參考:

//大堆調整:
void BigADjustUp(Heap* hp, int loc)
{assert(hp);HPDataType* a = hp->_a;int child = loc;int parent = (child - 1) / 2;while (child > 0){if (a[parent] < a[child]){swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

解釋:

父節點(parent)、子節點(child)在代碼中表示的是數組下標;

loc,即location位置的意思,記錄Push數據在數組中的下標;

計算新父節點:(左孩子或者右孩子的下標-1)/ 2就==他們父節點的下標,比如數組下標1與數組下標2的父節點在數組中就是下標0;

④每push進一個數據,先讓他做當前位置對應的左或者右孩子(該位置肯定是是葉節點),然后通過向上調整算法,若數據大于父節點中的數據,則交換、上升。

用該算法調整后:

int a[] = { 17,15,28,22,13,5,9 };

調整后:{28,22,17,15,13,5 ,9};


HeapPopBig:刪除節點

將堆頂元素與數組最后一個元素交換,_size--,然后調用向下調整算法。

// 堆的刪除:為堆排序
void HeapPopBig(Heap* hp)
{assert(hp);assert(hp->_a);swap(&hp->_a[0], &hp->_a[hp->_size - 1]);hp->_size--;BigADjustDown(hp, 0);
}

將堆頂元素與最后一個元素交換位置后,重要的是調用BigADjustDown()函數,使得堆能繼續成立。

核心算法BigADjustDown:向下調整算法

向下調整算法有一個前提:左右子樹必須是堆,才能調整。

堆的刪除,即刪除堆頂元素需要用到第二個核心算法——向下調整算法。

我們先說明堆刪除的思想:將堆頂元素與數組最后一個元素交換,然后_size--。

也就是說,向下調整算法的核心目標是:將堆頂所在的元素調整到適合它的位置。

算法思路:

選擇該父節點對應的左/右孩子節中值較大的那一個,與父節點的值交換(一開始是根節點);

②將交換的孩子節點當作新一輪的父節點,重復①;

③直到數組末尾,或者中途遇到小于或等于,滿足小堆條件退出。

void BigADjustDown(Heap* hp, int loc)
{assert(hp);HPDataType* a = hp->_a;//parent與child均為下標int parent = loc;int lchild = parent * 2 + 1, rchild = (parent + 1) * 2, max_child = 0;while (parent < hp->_size){max_child = a[lchild] > a[rchild] ? lchild : rchild;if (hp->_a[max_child] > hp->_a[parent] && max_child < hp->_size){swap(&hp->_a[parent], &hp->_a[max_child]);parent = max_child;lchild = parent * 2 + 1;rchild = (parent + 1) * 2;}else{break;}}}

解釋:

①新父節點下標:已知父節點的數組下標,那么該節點的左孩子數組下標=父節點下標*2+1,右孩子數組下標=(父節點下標+1)*2。如父節點數組下標為0,則左孩子為0*2+1,右孩子為(0+1)*2。

②BigADjustDown是配合HeapPopBig使用的,所以不要忘了實際場景是:堆頂與末尾數據進行了交換,需要BigADjustDown重新調整堆。

這也是為什么排升序要建大堆,排降序建小堆的原因:因為實現機制是將堆頂數據倒插到數組尾,大堆堆頂是數組數據最大的,故為升序;小堆堆頂是數組最小的,故為降序。


HeapDestory:堆的銷毀

記得將無用指針置空,防止野指針。

// 堆的銷毀
void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_capacity = hp->_size = 0;hp->_a = NULL;
}


2.堆排序代碼

//堆排序:升序
void HeapSortUp(int* a, int n)
{if (!a)return;Heap he;HeapCreateBig(&he, a, n);int cnt = n;while (cnt){HeapPopBig(&he);cnt--;}for (int i = 0; i < n; ++i)a[i] = he._a[i];HeapDestory(&he);
}

解釋:

①將傳入的數組通過HeapCreateBig構建起大堆;

②while循環,通過利用HeapPopBig每循環一次將堆頂(最大數據)移動至數組末尾,由于HeapPopBig一次成員變量_size便會--(數組尾下標不斷向前),當循環結束后成員變量數組_a存儲的數據便是升序排列;

這里利用HeapPopBig刪除機制,將堆頂逐漸移動至數組尾,但注意不是真正的刪除了而是堆中的_size--了(下次再向堆中push數據就會將原數據覆蓋),實際如果不再push那么這些數據是依然存在的。

③最后的for循環將排好的堆內數組挨個賦值給外部數組,之后再銷毀堆。

?以上述調整后:{28,22,17,15,13,5 ,9}的數組為例模仿代碼執行,

while中HeapPopBig執行情況:

三、分析堆排序

特性總結:
1.同樣是選擇排序,相比直接選擇排序,堆排序使用堆來選數,效率高了很多。
2. 時間復雜度:建堆過程為O(n),每次調整(彈出)為O(log n),總共n次調整,所以整體時間復雜度為O(n log n)
3. 空間復雜度:本文中的代碼實現為O(N)(也可以采取另一種原地建堆的方法,那樣空間復雜度為O(1));
4. 穩定性:不穩定,交換堆頂和末尾元素時,可能破壞相同元素的相對順序。


總結

本文是【排序算法】系類的第四篇,主要介紹了什么是堆,以及如何實現堆,最后分析了堆的特性。

整理不易,希望對你有所幫助。

讀完點贊,手留余香~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93108.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93108.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93108.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android領域驅動設計與分層架構實踐

引言在Android應用開發中&#xff0c;隨著業務邏輯日益復雜&#xff0c;傳統的MVC或簡單MVP架構往往難以應對。領域驅動設計(Domain-Driven Design, DDD)結合分層架構&#xff0c;為我們提供了一種更系統化的解決方案。本文將探討如何在Android項目中應用DDD原則與分層架構&…

Android12 Framework電話功能UI定制

文章目錄簡介代碼中間按鈕Fragment創建VideoCallFragmentFragment管理添加按鍵掛斷電話功能相關文章簡介 Android版本&#xff1a;12 芯片平臺&#xff1a;展銳 如下圖為通話中的UI&#xff0c;打電話出去時顯示的UI與此也差不多&#xff0c;但來電時UI是不一樣的 這個界面是…

高并發場景下分布式ID生成方案對比與實踐指南

高并發場景下分布式ID生成方案對比與實踐指南 在分布式系統中&#xff0c;唯一且全局有序的ID生成器是很多業務的底層組件。隨著系統并發量不斷攀升&#xff0c;如何在高并發場景下保證ID的唯一性、性能、可用性和可擴展性&#xff0c;成為后端架構師需要重點考慮的問題。本文將…

Emscripten 指南:概念與使用

Emscripten 指南&#xff1a;概念與使用 什么是 Emscripten&#xff1f; Emscripten 是一個開源的編譯器工具鏈&#xff0c;用于將 C/C 代碼編譯成高效的 WebAssembly&#xff08;Wasm&#xff09;和 JavaScript。它基于 LLVM 編譯器架構&#xff0c;允許開發者&#xff1a; ?…

使用鏡像網站 打開克隆 GitHub 網站倉庫內容 git clone https://github.com/

GitHub 網站有時因 DNS 解析問題或網絡限制&#xff0c;國內訪問可能會受限。使用鏡像網站打開網站 使用鏡像網站&#xff1a;GitHub 有一些鏡像網站&#xff0c;可替代官網訪問&#xff0c;如https://hub.fastgit.org、https://gitclone.com、https://github.com.cnpmjs.org等…

Linux隨記(二十二)

一、redhat6.5 從openssh5.3 升級到openssh10 - 報錯處理【升級后賬號密碼一直錯誤 和 sshd dead but subsys locked】 虛擬機測試情況 - 正常&#xff1a;情況一、 升級后賬號密碼一直錯誤 情況二、 執行service sshd status出現 sshd dead but subsys locked

機器學習之TF-IDF文本關鍵詞提取

目錄 一、什么是 TF-IDF&#xff1f; 1.語料庫概念理解 二、TF-IDF 的計算公式 1. 詞頻&#xff08;TF&#xff09; 2. 逆文檔頻率&#xff08;IDF&#xff09; 3. TF-IDF 值 三、關鍵詞提取之中文分詞的實現 四、TF-IDF簡單案例實現 &#xff08;1&#xff09;數據集…

Flutter屏幕和字體適配(ScreenUtil)

一、簡介 flutter_screenutil 是一個 Flutter 插件&#xff0c;專門用于處理屏幕適配問題。它簡化了不同設備間尺寸差異的處理&#xff0c;確保你的應用在各種屏幕上都能保持良好的顯示效果。開發者可以通過簡單的調用來設置基于設計圖尺寸的控件寬高和字體大小。 項目地址&a…

mimiconda+vscode

安裝miniconda實現python包管理&#xff0c;并通過vscode進行編寫python代碼 miniconda簡單介紹 Miniconda 是 Anaconda 公司的一個輕量級 Python 發行版本&#xff0c;它包含了最基本的包管理器 conda 和 Python 環境&#xff0c;只帶最核心的組件&#xff0c;沒有額外的大量科…

Windows文件時間修改指南:從手動到自動化

修改文件的時間屬性可以滿足多種需求。比如&#xff0c;它可以幫助整理文件&#xff0c;使得文件按照特定的時間順序排列&#xff0c;有助于更好地管理資料。它的體積真小&#xff0c;才300多KB。能用來調整文件的創建時間、最后訪問和修改時間。文件時間屬性修改_NewFileTime.…

能刷java題的網站

以下是一些適合刷Java題的優質網站&#xff0c;涵蓋從基礎到進階、算法面試及實戰項目等多種需求&#xff1a; ?一、綜合編程練習平臺? ?LeetCode?&#xff08;leetcode.com&#xff09; ?特點?&#xff1a;全球最知名的算法題庫&#xff0c;含海量Java題目&#xff0c;分…

掘金數據富礦,永洪科技為山東黃金定制“數智掘金”實戰營

在黃金開采的轟鳴聲中&#xff0c;另一場靜水深流的“掘金行動”正悄然展開。山東黃金集團&#xff0c;這個行業的巨頭&#xff0c;在深挖地層寶藏的同時&#xff0c;也敏銳捕捉到數據洪流中蘊藏的價值富礦。然而&#xff0c;當海量業務數據匯聚&#xff0c;如何從中精準提煉決…

【論文閱讀】BEVFormer論文解析及Temporal Self-Attention、Spatial Cross-Attention注意力機制詳解及代碼示例

BEVFormer: Learning Bird’s-Eye-ViewRepresentation from Multi-Camera Images via Spatiotemporal Transformers|Temporal Self-Attention、Spatial Cross-Attention注意力機制詳解 BEVFormer&#xff08;Bird’s-Eye-View Former&#xff09;是一種先進的計算機視覺模型&am…

在 Ubuntu 中docker容器化操作來使用新建的 glibc-2.32

在 Ubuntu 中使用容器化操作來使用新建的 glibc-2.32,可以通過創建自定義 Docker 鏡像來實現。以下是完整的解決方案: 方案 1:創建包含 glibc-2.32 的 Docker 鏡像 1. 創建 Dockerfile dockerfile # 使用 Ubuntu 基礎鏡像 FROM ubuntu:20.04# 安裝編譯依賴 RUN apt-get …

GOOUUU ESP32-S3-CAM 果云科技開發板開發指南(二)(超詳細!)Vscode+espidf 攝像頭拍攝視頻實時傳輸到LCD,文末附源碼

書接上回&#xff0c;上一篇blog是使用esp32s3通過ov2640攝像頭拍攝到一幀照片&#xff0c;并把它保存到了SD卡中&#xff0c;這第二篇就通過LCD將拍攝到的圖片顯示到LCD上&#xff0c;本次分享硬件使用的 ESP32-S3-CAM 果云科技開發板&#xff0c;并且使用了配套的LCD擴展板&a…

攻防世界-ics-05(遠程文件執行)

一.審題大致瀏覽一下網頁&#xff0c;發現就這邊會有東西。看一下源碼會不會有東西或者稍微點擊一下這個頁面的內容看會不會出現東西。點擊了一下這個云平臺設備維護中心發現url變了&#xff0c;是get的方法傳page參數二.嘗試漏洞類型自己這邊試了sql注入發現不是&#xff0c;試…

Dell PowerEdge: Servers by generation (按代系劃分的服務器)

Dell PowerEdge: Servers by generation {按代系劃分的服務器}1. Table of 17th, 16th, 15th, and 14th Generation PowerEdge servers2. List of all PowerEdge server models including Type, CPU vendor, Generation, and Remote ManagementReferencesPowerEdge: Servers by…

Rust學習筆記(二)|變量、函數與控制流

本篇文章包含的內容1 變量與常量2 類型2.1 標量類型2.2 復合類型3 函數4 控制流4.1 分支4.2 循環1 變量與常量 在Rust中&#xff0c;使用let關鍵字聲明一個變量&#xff0c;變量默認是不可變的。如果要聲明可變變量&#xff0c;需要使用mut關鍵字將其聲明為可變變量。 let x …

【渲染流水線】[幾何階段]-[圖元裝配]以UnityURP為例

【從UnityURP開始探索游戲渲染】專欄-直達 前情提要 【渲染流水線】主線索引-從數據到圖像以UnityURP為例-CSDN博客 圖元裝配負責將離散頂點組裝成完整幾何圖元&#xff08;如點、線、三角形、三角形條帶&#xff09; &#xff08;對渲染的探索是個持續不斷完善的過程&#x…

jvm有哪些垃圾回收器,實際中如何選擇?

7.G1收集器一款面向服務端應用的垃圾收集器。 特點如下&#xff1a; 并行與并發&#xff1a;G1能充分利用多CPU、多核環境下的硬 件優勢&#xff0c;使用多個CPU來縮短Stop-The-World停頓時間。部分收集器原本需要停頓Java線程來執行GC動作&#xff0c;G1收 集器仍然可以通過并…