01數據結構-堆排序

01數據結構-堆排序

  • 前言
  • 1.堆
  • 2.堆的操作邏輯
  • 3.堆的代碼實現

前言

數據結構中的堆是一種結構,C語言的堆是空間管理的程序員malloc,free的空間,兩者沒多大關系。

1.堆

  • 邏輯上

    堆(Heap)是一類基于完全二叉樹的特殊數據結構。通常將堆分為兩種類型:

    1.大頂堆(Max Heap):在大頂堆中,根結點的值必須大于他的孩子結點的值,對于二叉樹中所有子樹都滿足此規律
    在這里插入圖片描述

    2.小頂堆(Min Heap):在小頂堆中,根結點的值必須小于他的孩子結點的值,對于二叉樹中所有子樹都滿足此規律
    在這里插入圖片描述

  • 物理上

    之前有個性質:完全二叉樹中用數組空間來存儲結構,從1號下標開始存儲第一個元素,i號下標的根節點是2\i,左孩子2i,右孩子2i+1,接下來我們來看下怎么處理堆的邏輯。

2.堆的操作邏輯

如圖,有8個數組組成了最小堆初始化數,我已經申請了9個空間(因為是完全二叉樹用數組空間存儲,從1號下標開始存元素),定義兩個 指針:len指針用來表示待插入位置,capacity用來表示數組空間容量
在這里插入圖片描述
插入邏輯:

1.在最后一個位置上插入新元素,從代碼角度講 ++len;data[len]=v;
2.如果是小頂堆,找這個元素的父節點,如果父節點的值大于這個元素,交換兩個節點的值
3.繼續交換節點的父節點繼續執行條件判斷,是否交換
4.直到找到根或者已不滿足條件

2,3,4我們的稱為上浮操作

如圖初始化時把9放進來
在這里插入圖片描述
把3放進來,發現3比9小,于是交換兩個節點的值,并在數據區里也更新對應的值
在這里插入圖片描述
把7放進來
在這里插入圖片描述
把6放進來,發現父節點9的值比它大,交換

在這里插入圖片描述
一直這樣直到把全部數字都放進堆中,最終結果如圖

在這里插入圖片描述
提取數據:
1.提取根元素,提取到最值
2.我們不能直接給它刪了,我們要繼續保持這顆樹的邏輯結構
3.把最后一個元素放到根節點的值,有效長度再減1,等價于刪除最后一個元素(因為刪除完全二叉樹的節點時都是刪除葉子節點,這樣才可以保持樹的結構,但是沒有滿足小頂堆的約束)。
4.把現在的根節點的值放入到正確的位置

3,4我們稱為下沉。

如圖我們要提取根節點1,按照上述邏輯,我們要保持整棵樹的結構,先把葉子節點9的值交給1,刪除葉子節點,len–
在這里插入圖片描述
開始完成約束條件,把根節點的值放入正確的位置,此時我們需要比較根節點的左右孩子的大小,選擇較小的那個,因為如果我們把9和3交換位置,3比2大依然沒有滿足小頂堆的約束,所以9和2交換位置,此時9來到2的位置,再次比較左右孩子的大小,發現5是較小值,交換5和9。如圖:
在這里插入圖片描述
如果我們有100個元素,但是我們只想要提取前5個,你可以把這100個排好序,然后取前5個即可,但是這樣時間復雜度是O(n2),我們完全可以用小頂堆排好這100個元素,然后提取5次小頂堆的根節點,時間開銷就很少,這樣效率就較好。例如應用:Top10,在音樂榜單中的前10首歌曲,就可以采取這種思想,把成千上萬首歌放進小頂堆,然后取10次小頂堆的根節點就拍好序了。

3.堆的代碼實現

數據結構實現

#ifndef MINI_HEAP_H
#define MINI_HEAP_H
#include "../sortHelper.h"
// 定義小頂堆的結構
typedef struct {keyType *data;int len;            // 堆data的長度int capacity;       // 最大容量
} MiniHeap;MiniHeap *createMiniHeap(int n);            // 產生n個元素的堆空間
void releaseMiniHeap(MiniHeap *miniHeap);// 插入
void insertMiniHeap(MiniHeap *heap, keyType e);
// 提取
keyType extractMinMiniHeap(MiniHeap *heap);
#endif //MINI_HEAP_H

創建樹的接口:MiniHeap *createMiniHeap(int n);

MiniHeap * createMiniHeap(int n) {MiniHeap * heap = malloc(sizeof(MiniHeap));if (heap==NULL) {return NULL;}heap->data = malloc(sizeof(int)*n);if (heap->data == NULL) {free(heap);return NULL;}heap->capacity=n;heap->len=0;return heap;
}

釋放樹的接口:void releaseMiniHeap(MiniHeap *miniHeap);

void releaseMiniHeap(MiniHeap *miniHeap) {if (miniHeap) {if (miniHeap->data) {free(miniHeap->data);}free(miniHeap);}
}

插入接口:void insertMiniHeap(MiniHeap *heap, keyType e);

static void shiftUp(MiniHeap *miniHeap, int k) {keyType tmp;// k/2表示父節點,k節點子節點while (k > 1 && miniHeap->data[k / 2] > miniHeap->data[k]) {tmp = miniHeap->data[k];miniHeap->data[k] = miniHeap->data[k / 2];miniHeap->data[k / 2] = tmp;k /= 2;}
}void insertMiniHeap(MiniHeap *heap, keyType e) {//防越界if (heap->len + 1 > heap->capacity) {return;}heap->data[++heap->len] = e;shiftUp(heap,heap->len);
}

我這里寫了一個內在的接口shifUp即我在2.堆的操作邏輯中的2,3,4操作中的上浮操作,相信大家應該能看懂

提取接口:keyType extractMinMiniHeap(MiniHeap *heap);

static void shiftDown(MiniHeap *miniHeap, int k) {keyType tmp;while (2 * k <= miniHeap->len) {        // 保證有左孩子int index = 2 * k;if (index + 1 <= miniHeap->len && miniHeap->data[index + 1] < miniHeap->data[index]) {++index;}if (miniHeap->data[k] <= miniHeap->data[index]) {break;}tmp = miniHeap->data[k];miniHeap->data[k] = miniHeap->data[index];miniHeap->data[index] = tmp;k = index;}
}keyType extractMinMiniHeap(MiniHeap *heap) {//防越界if (heap->len <= 0) {return 0;}keyType ret=heap->data[1];heap->data[1]=heap->data[heap->len--];shiftDown(heap,1);return ret;
}

注意在static void shiftDown(MiniHeap *miniHeap, int k)我們不僅要檢驗左子樹有沒有越界還要檢驗右子樹有沒有越界,先保證有左孩子,如果我們的右孩子沒有越界并且右孩子的值小于左孩子的值,那就拿到右孩子的索引,如果父節點的值已經小于了左右孩子節點中的較小值就直接退出,然后進行交換值。

來測一下:

#include <stdio.h>
#include "miniHeap.h"void test01() {int data[] = {9, 3, 7, 6, 5, 1, 10, 2};int n = 20;MiniHeap *miniHeap = createMiniHeap(n);for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {insertMiniHeap(miniHeap, data[i]);}printf("extra: ");for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {printf("%d ", extractMinMiniHeap(miniHeap));}printf("\n");releaseMiniHeap(miniHeap);
}int main() {test01();return 0;
}

結果:

D:\work\DataStruct\cmake-build-debug\04_Sort\HeapSort.exe
extra: 1 2 3 5 6 7 9 10進程已結束,退出代碼為 0

我們再寫一個接口來測試堆排序的時間復雜度:

#include "heapSort.h"void miniHeapSort(SortTable *table) {MiniHeap *heap = createMiniHeap(table->length);for (int i = 0; i < table->length; i++) {insertMiniHeap(heap, table->data[i].key);}for (int i = 0; i < table->length; i++) {table->data[i].key = extractMinMiniHeap(heap);}releaseMiniHeap(heap);
}

用快速排序來進行對比:

#include <stdio.h>
#include "miniHeap.h"
#include "heapSort.h"
#include "../02_SwapSort/quickSort.h"void test01() {int data[] = {9, 3, 7, 6, 5, 1, 10, 2};int n = 20;MiniHeap *miniHeap = createMiniHeap(n);for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {insertMiniHeap(miniHeap, data[i]);}printf("extra: ");for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {printf("%d ", extractMinMiniHeap(miniHeap));}printf("\n");releaseMiniHeap(miniHeap);
}void test02() {int n = 100000;SortTable *table1 = generateRandomArray(n, 0, n + 5000);SortTable *table2 = copySortTable(table1);testSort("Heap Sort", miniHeapSort, table1);testSort("quick Sort", quickSortV1, table2);releaseSortTable(table1);releaseSortTable(table2);
}int main() {test02();return 0;
}

結果:

D:\work\DataStruct\cmake-build-debug\04_Sort\HeapSort.exe
Heap Sort cost time: 0.013000s.
quick Sort cost time: 0.009000s.進程已結束,退出代碼為 0

可以看到兩者的排序時間是差不多的,這是因為堆排序和樹的高度有關,所以時間復雜度也是O(nlogn)。

大概先寫這些吧,今天的博客就先寫到這,謝謝您的觀看。

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

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

相關文章

在線課程|基于SprinBoot+vue的在線課程管理系統(源碼+數據庫+文檔)

在線課程 目錄 基于SprinBootvue的在線課程管理系統 一、前言 二、系統設計 三、系統功能設計 1 管理員模塊的實現 2在線課程 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取&#xff1a; 博主介紹&#xff1a;??大廠碼農|…

Python海象運算符:=

文章目錄簡介??條件判斷優化循環控制簡化?推導式高效計算?正則匹配與數據提取?性能對比參考文獻簡介 海象運算符 :&#xff0c;又稱??賦值表達式??&#xff08;Assignment Expression&#xff09;&#xff0c;Python 3.8 后可用&#xff0c;PEP 572 引入&#xff0c;…

Vue 2 項目中快速集成 Jest 單元測試(超詳細教程)

在 Vue 項目中編寫單元測試&#xff0c;是提升代碼質量和維護性的關鍵一步。本文將帶你從零開始&#xff0c;在一個 Vue 2 Vue CLI 項目中集成 Jest 作為單元測試框架&#xff0c;并運行第一個測試用例。? 適用于 Vue 2 項目&#xff08;如你使用的是 vue-cli-service&#x…

PostgreSQL15——管理表空間

管理表空間一、基本概念二、創建表空間三、修改表空間四、刪除表空間一、基本概念 在 PostgreSQL 中&#xff0c;它是通過表空間&#xff08;Tablespaces&#xff09;來實現邏輯對象&#xff08;表、索引等&#xff09;與物理文件之間的映射。創建數據庫或者數據表&#xff08…

趣打印高級版--手機打印軟件!軟件支持多種不同的連接方式,打印神器有這一個就夠了!

軟件介紹&#xff08;文末獲取&#xff09;趣打印高級版是一款手機打印軟件。軟件支持五種不同的連接方式&#xff0c;每種都有穩定且快速的反應&#xff0c;用戶均可通過手機進行打印機的遠程使用和設置。軟件還支持上傳不同格式的文檔類型進行打印&#xff0c;方便快捷&#…

【開源框架】7 款流行的 Vue 3 后臺管理框架對比

以下是 7 個流行的 Vue 3 后臺管理框架在 Star 數&#xff08;截至 2025 年 8 月21日的 GitHub 最新數據&#xff09;、框架特點、基于的技術棧及開源協議四個方面的詳細對比&#xff1a; 1. Vue-Vben-Admin GitHub 地址&#xff1a;https://github.com/vbenjs/vue-vben-admin…

Datawhale工作流自動化平臺n8n入門教程(一):n8n簡介與平臺部署

前言 在數字化時代&#xff0c;重復性的工作任務正在消耗著我們大量的時間和精力。從數據同步到營銷自動化&#xff0c;從客戶服務到內容管理&#xff0c;這些瑣碎但必要的任務往往讓我們疲于應對。而工作流自動化工具的出現&#xff0c;為我們提供了一個優雅的解決方案。 今天…

SRE - 定位與能力

僅為個人知識總結與記錄 Site Reliability Engineer&#xff1a;站點可靠性工程&#xff08;SRE 軟件工程師 運維專家 可靠性專家&#xff09; 相對傳統的運維工程師&#xff0c;SER 注重開發&#xff0c;效率&#xff0c;追求自動化。對于 SRE 工程師&#xff0c;追究的就是…

StarRocks學習4-查詢優化與性能調優

? 1. 執行計劃分析&#xff08;EXPLAIN&#xff09; &#x1f31f; 作用&#xff1a; 用于查看 SQL 的執行路徑&#xff0c;判斷是否命中索引、物化視圖、Join 策略、并行度等。 &#x1f4cc; 常用命令&#xff1a; EXPLAIN SELECT ...; EXPLAIN VERBOSE SELECT ...;&#x1…

CentOS系統安裝Git全攻略

文章目錄? 方法一&#xff1a;使用 yum 或 dnf 包管理器安裝&#xff08;推薦&#xff09;1. 更新系統軟件包(非必須)[^1]2. 安裝 Git3. 驗證安裝? 方法二&#xff1a;從源碼編譯安裝&#xff08;適用于需要自定義版本或配置&#xff09;1. 安裝依賴包2. 下載 Git 源碼3. 編譯…

VR交通安全學習機-VR交通普法體驗館方案

VR交通安全學習機是一種基于虛擬現實技術的互動式教育設備&#xff0c;旨在通過虛擬環境模擬真實的交通場景&#xff0c;幫助用戶深入了解交通規則、交通信號、道路安全等知識&#xff0c;并通過沉浸式的體驗讓他們親身感受到不遵守交通規則的后果。無論是駕駛員、行人還是騎行…

算法題(188):團伙

審題&#xff1a; 本題需要我們通過解析所有人之間的關系&#xff0c;從而判斷出朋友團體的總個數并輸出 思路&#xff1a; 方法一&#xff1a;擴展域并查集 由于這里涉及對朋友/敵人等關系集合的頻繁操作&#xff0c;所以我們需要使用并查集來操作&#xff0c;但是普通的并查集…

C++開發/Qt開發:單例模式介紹與應用

單例模式是軟件設計模式中最簡單也是最常用的一種創建型設計模式。它的核心目標是確保一個類在整個應用程序生命周期中只有一個實例&#xff0c;并提供一個全局訪問點。筆者白話版理解&#xff1a;你創建了一個類&#xff0c;如果你希望這個類對象在工程中應用時只創建一次&…

Linux筆記---策略模式與日志

1. 設計模式設計模式是軟件開發中反復出現的問題的通用解決方案&#xff0c;它是一套套被反復使用、多數人知曉、經過分類編目的代碼設計經驗總結。設計模式并非具體的代碼實現&#xff0c;而是針對特定問題的抽象設計思路和方法論。它描述了在特定場景下&#xff0c;如何組織類…

關于多個el-input的自動聚焦,每輸入完一個el-input,自動聚焦到下一個

講解原理或者思路&#xff1a;如果你有多個el-input,想要實現每輸入完一個輸入框&#xff0c;然后自動聚焦到下一個輸入框&#xff0c;同理&#xff0c;如果每刪除一個輸入框的值&#xff0c;自動聚焦到上一個輸入框。條件那么首先要做的就是&#xff0c;設置條件&#xff0c;在…

AI 賦能教育變革:機遇、實踐與展望

引言說明教育在社會發展中的重要地位&#xff0c;以及傳統教育面臨的困境。引出 AI 技術為教育變革帶來新機遇&#xff0c;闡述研究其在教育中應用的價值。AI 為教育帶來的機遇個性化學習支持&#xff1a;講解 AI 通過分析學生學習數據&#xff0c;如答題情況、學習時間等&…

(一)八股(數據庫/MQ/緩存)

文章目錄 項目地址 一、數據庫 1.1 事務隔離級別 1. 事務的四大特性 2. Read Uncommited臟讀(未提交讀) 3. Read Commited幻讀(sql默認已提交讀) 4. Repeatable Read 5. Serializable 6. Snapshot(快照隔離) 7. 代碼開啟 8. For update和Repeatable Read的區別 1.2 各種鎖 …

STM32H750 CoreMark跑分測試

STM32H750 CoreMark跑分測試&#x1f50e;CoreMark跑分測試查詢網站&#xff1a;https://www.eembc.org/coremark/scores.php&#x1f4dc; CoreMark源碼&#xff1a;https://www.github.com/eembc/coremarkCoreMark移植和配置參考&#xff1a;https://community.st.com/t5/stm…

RabbitMQ如何確保消息發送和消息接收

消息發送確認 1 ConfirmCallback方法 ConfirmCallback 是一個回調接口&#xff0c;消息發送到 Broker 后觸發回調&#xff0c;確認消息是否到達 Broker 服務器&#xff0c;也就是只 確認是否正確到達 Exchange 中。 2 ReturnCallback方法 通過實現 ReturnCallback 接口&#xf…

Linux:進程間通信-管道

Linux&#xff1a;進程間通信-管道 前言&#xff1a;為什么需要進程間通信&#xff1f; 你有沒有想過&#xff0c;當你在電腦上同時打開瀏覽器、音樂播放器和文檔時&#xff0c;這些程序是如何協同工作的&#xff1f;比如&#xff0c;瀏覽器下載的文件&#xff0c;為什么能被文…