【數據結構】堆的完整實現

堆的完整實現

  • 堆的完整實現
  • GitHub地址
  • 前言
  • 堆的核心功能實現
    • 重溫堆的定義
    • 堆結構定義
    • 1. 堆初始化與銷毀
    • 2. 元素交換函數
    • 3. 堆化操作
      • 向上調整(子→父)
      • 向下調整(父→子)
    • 4. 堆元素插入
    • 5. 堆元素刪除
    • 6. 輔助功能函數
      • 堆的判空
      • 獲取堆頂元素
      • 獲取堆的大小
  • 結語

堆的完整實現

GitHub地址

有夢想的電信狗

前言

堆(Heap)是一種特殊的完全二叉樹數據結構,常用于實現優先級隊列。本文基于C語言實現大跟堆,包含核心操作:插入元素、刪除堆頂元素、堆化操作等。以下是完整實現及詳細解析。


堆的核心功能實現

重溫堆的定義

普通的二叉樹是不適合用數組來存儲的,因為可能會存在大量的空間浪費。而完全二叉樹更適合使用順序結構存儲。

現實中我們通常把堆(一種特殊的二叉樹)使用順序結構的數組來存儲。

在這里插入圖片描述

需要注意的是這里的堆和操作系統虛擬進程地址空間中的堆是兩回事,一個是數據結構,一個是操作系統中管理內存的一塊區域分段

在這里插入圖片描述

二叉樹的順序存儲,是堆的前身

在順序存儲的二叉樹上加一些限定條件,定義為堆

  • 小根堆 : 樹中所有父結點都小于或等于子節點
  • 大根堆 樹中所有父結點都大于或等于子節點
  • 堆可以用來排序,但堆并非是有序的!

堆的性質

  • 堆中某個節點的值總是不大于或不小于其父節點的值;
  • 堆總是一棵完全二叉樹。

在這里插入圖片描述

堆結構定義

//實現大堆   這里以實現大根堆為例子
typedef int HeapDataType;	//定義堆中存放的數據類型,方便修改
typedef struct HeapNode {HeapDataType* base;   // 堆存儲數組基地址int size;              // 當前元素個數int capacity;          // 堆容量
}Heap;

結構說明

  • base:動態數組基地址,用于存儲堆元素,用數組來順序存儲
  • size:當前堆中元素數量
  • capacity:數組總容量

數組存儲的二叉樹的下標特性

  • parent = (child - 1) / 2
  • lefichild = parent * 2 + 1
  • lefichild = parent * 2 + 2

功能一覽

//堆初始化與銷毀
void HeapInit(Heap* pheap);
void HeapDestroy(Heap* pheap);//堆插入和刪除
void HeapPush(Heap* pheap, HeapDataType data);
void HeapPop(Heap* pheap);
//堆的向上 向下調整
void AdjustUp(HeapDataType* arr, int child);
void AdjustDown(HeapDataType* arr, int size, int parent);//交換堆中的元素
void Swap(HeapDataType* left, HeapDataType* right);
//獲取堆頂元素
HeapDataType HeapTop(Heap* pheap);
//判斷堆是否為空
bool HeapEmpty(Heap* pheap);
//獲取堆的size
int HeapSize(Heap* pheap);

1. 堆初始化與銷毀

初始化

//堆初始化
void HeapInit(Heap* pheap) {assert(pheap);	pheap->base = (HeapDataType*)malloc(sizeof(HeapDataType) * 4);if (pheap->base == NULL) {perror("malloc failed\n");return;}pheap->size = 0;pheap->capacity = 4;
}

實現思路

  • 斷言指針有效性,堆結構指針必須存在
  • 為堆分配初始容量(暫設置為4個元素空間),申請空間失敗時報錯并返回
  • 初始化size0表示空堆,capacity初始化為申請的空間
  • 時間復雜度:O(1)

注意事項

  • 必須進行指針有效性斷言檢查
  • 初始容量不宜過小(建議為4的倍數)

銷毀

//清理資源
void HeapDestroy(Heap* pheap) {assert(pheap);free(pheap->base);pheap->base = NULL;pheap->capacity = pheap->size = 0;
}

實現思路

  • assert斷言堆結構指針不為空
  • free釋放動態分配的存儲空間
  • 將指針置NULL防止野指針
  • sizecapacity都置為0
  • 時間復雜度:O(1)

2. 元素交換函數

void Swap(HeapDataType* child, HeapDataType* parent) {HeapDataType temp = *child;*child = *parent;*parent = temp;
}

功能:交換父子節點數值
使用場景:堆化(向上調整/向下調整)時的元素位置調整

交換函數功能較為簡單,此處不過多贅述。


3. 堆化操作

向上調整 或向下調整的條件是,左右子樹 必須是 大堆 或者 小堆

向上調整(子→父)

在這里插入圖片描述

// 插入數據向上調整, 刪除數據向下調整
//向上調整 或向下調整的條件是,左右子樹 必須是大堆 或者 小堆
void AdjustUp(HeapDataType* arr, int child) {	//child是需要調整的節點的下標assert(arr);int parent = (child - 1) / 2;//while (parent >= 0) {		//	 個人建議while的循環條件內不要寫太復雜的條件//寫成  child > 0  會更好  因為 最壞時 child 為 0 ,此時parent = (child-1)/2  也為0//因此 實際上 parent 不會為 <= 0while (child > 0) {   // child 等于 0 或小于 0 時就不用再調整了if (arr[child] > arr[parent]) {Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}//child <= parent 時else	break;}
}

功能:將新插入堆的元素調整到合適位置 ,使其滿足堆的性質

  • child是新插入元素的下標,一般是size-1(即通過尾插進入的最后一個元素的下標)
  • arr是待調整的數組指針,斷言arr非空
  • child 為子節點,向上調整思路
    • 通過parent = (child - 1) / 2,計算待調整結點的父節點的下標
    • child下標為0時,代表最后一次交換(調整)已結束。因此循環結束條件為child == 0
    • 比較父子結點的大小,子節點大于父節點時,交換,滿足大根堆的邏輯,同時下標childparent進行更新
    • 當前child結點 < parent結點時,代表以滿足大根堆,直接結束循環即可。
  • 時間復雜度為 O(logN)

終止條件

  • 以下條件滿足其一,循環即可終止。
    • 子節點值 父節點值
    • 到達堆頂(child=0

向下調整(父→子)

在這里插入圖片描述

// 向下調整 到葉子結點結束,葉子結點的左孩子 的下標  大于 size   size是數組的大小
void AdjustDown(HeapDataType* arr, int size, int parent) {assert(arr);assert(parent >= 0 && parent < size);	//parent非負 且 不能越界int child = parent * 2 + 1;while (child < size) {//檢查 child+1 是否越界 以及 找出左右孩子中更大的那個//child + 1 >= size 時,表示當前父節點只有左孩子if (child + 1 < size && arr[child + 1] > arr[child])++child;if (arr[child] > arr[parent]) {Swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}

實現思路

  • 斷言arrparent >= 0 && parent < size,保證arr存在,parent非負且不越界
    • parent一般是0從堆頂元素開始調整,盡可能地保持了堆的結構。
  • child = parent * 2 + 1:計算左孩子的下標,右孩子的下標是左孩子下標+1
  • child >= size時,代表最后一個元素已完成交換,循環結束
  • 檢查child+1是否越界以及 找出左右孩子中更大的那個,讓較大者與parent結點進行比較。
  • child更大時,與parent結點進行交換,childparent接著移動。

實現要點

  1. 總是與較大的子節點比較
  2. 循環終止條件(滿足其一即終止):
    • parent數據 ≥ 兩個child節點數據,此時已符合大根堆的條件,向下調整完成
    • child > size時,所有的節點已完成交換,此時已符合大根堆的條件,向下調整完成

時間復雜度為 O(logN)

小根堆的調整:可與大根堆類比
在這里插入圖片描述


4. 堆元素插入

  • 向上調整的過程

在這里插入圖片描述

// 插入,不能指定位置插入。
// 因為新元素插入后要進行調整使其滿足堆的結構,指定的位置不一定是最終調整后的位置
void HeapPush(Heap* pheap, HeapDataType data) {assert(pheap);	//空堆也可以push,但需保證結構體存在//插入檢查是否需要擴容if (pheap->size == pheap->capacity) {HeapDataType* newSpace = (HeapDataType*)realloc(pheap->base, sizeof(HeapDataType) * pheap->capacity * 2);if (newSpace == NULL) {perror("realloc failed\n");return;}pheap->base = newSpace;pheap->capacity *= 2;}//更新pheap->base[pheap->size] = data;pheap->size++;//插入后需向上調整,保證插入后滿足堆的特性AdjustUp(pheap->base, pheap->size - 1);	//size++ 后,size-1 是新插入元素的下標
}

實現思路

  1. 斷言堆存在檢查并擴容(2倍擴容策略)
    • 擴容:
      • 開空間,二倍擴容
      • 判斷是否開辟成功
      • 更改指針和容積
  2. 將新元素插入數組末尾,更新size盡可能的保持原來堆的結構
  3. 執行向上調整操作維護堆結構

時間復雜度

  • 最優:O(logN)(不需要擴容)
  • 最差:O(N)(觸發擴容)

5. 堆元素刪除

  • 向下調整的過程

在這里插入圖片描述

//堆的刪除  應當刪除堆頂的元素,刪除堆尾的數據沒有意義。
//刪除最大的或最小的,可以選出第二大或第二小的
//挪動刪除(直接刪)的缺點:  1. 效率低下O(n)   2.  堆的父子關系全亂了
// 刪堆頂的元素,將第一個元素和最后一個元素交換(最大限度的保持了原有的關系),再向下調整維持堆的大小關系
void HeapPop(Heap* pheap) {assert(pheap && pheap->size > 0);assert(!HeapEmpty(pheap));//刪除堆頂元素   交換堆頂元素 和 堆尾元素Swap(&pheap->base[0], &pheap->base[pheap->size - 1]);pheap->size--;		//刪除數據,讓size-1   size--之后,可能會為0// 僅當堆非空時進行向下調整if (pheap->size > 0) {AdjustDown(pheap->base, pheap->size, 0);}
}

實現思路

  • 斷言堆存在并且確保堆為非空。
  • 通過交換堆頂元素和堆尾元素,并更改size--來實現數組內元素的刪除
  • 通過size--的方式刪除元素,向下調整時,要確保size的值不為0

注意事項

  • 刪除前必須檢查堆是否為空
  • size減至0時無需向下調整操作

6. 輔助功能函數

堆的判空

//判斷堆是否為空
bool HeapEmpty(Heap* pheap) {assert(pheap);return pheap->size == 0;
}

獲取堆頂元素

//獲取堆頂元素
HeapDataType HeapTop(Heap* pheap) {assert(pheap);assert(pheap->base);return pheap->base[0];
}
  • 0號元素就是堆頂元素

獲取堆的大小

//獲取堆的size
int HeapSize(Heap* pheap) {assert(pheap);return pheap->size;
}

功能說明

  • HeapTop:獲取堆頂元素(極值)
    • 大根堆時是極大值
    • 小跟堆時是極小值
  • HeapEmpty:判斷堆是否為空
  • HeapSize:獲取當前元素個數

結語

本文完整實現了基于數組存儲的大根堆結構,重點闡釋了堆化過程中向上調整與向下調整的核心邏輯。通過動態數組管理、二倍擴容策略及父子節點下標計算,構建了插入元素時末尾上浮、刪除堆頂時首尾交換后根節點下沉的高效操作,確保堆性質在O(logN)時間內得以維護。關鍵點在于理解完全二叉樹順序存儲的特性,以及插入/刪除時通過逐層比較交換維護父節點≥子節點的規則。實際應用中可調整比較邏輯切換大小堆,適用于優先隊列、堆排序等場景,注意邊界處理避免空堆刪除和擴容失敗問題。

分享到此結束啦
一鍵三連,好運連連!

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

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

相關文章

如何優化MySQL主從復制的性能?

優化MySQL主從復制的性能需要從硬件、配置、架構設計和運維策略等多方面入手。以下是詳細的優化方案&#xff1a; 一、減少主庫寫入壓力 1. ?主庫優化? 二進制日志&#xff08;binlog&#xff09;優化?&#xff1a; 使用 binlog_formatROW 以獲得更高效的復制和更少的數…

MySQL安裝完全指南:從零開始到配置優化(附避坑指南)

&#x1f525; 前言&#xff1a;為什么你總是裝不好MySQL&#xff1f; &#xff08;實話實說&#xff09;每次看到新手在MySQL安裝環節瘋狂踩坑&#xff0c;老司機都忍不住想摔鍵盤&#xff01;明明官網下載的安裝包&#xff0c;怎么就會報錯呢&#xff1f;為什么別人的環境變…

密碼學_加密

目錄 密碼學 01 密碼基礎進制與計量 02 加解密基操 替換 移位 編碼 編碼 置換 移位 加解密強度 03 對稱加密算法(私鑰) 工作過程 缺陷 對稱加密算法列舉&#xff1f; DES DES算法架構 DES分組加密公式 DES中ECB-CBC兩種加密方式 3DES 由于DES密鑰太短&#xf…

輕量級RTSP服務模塊:跨平臺低延遲嵌入即用的流媒體引擎

在音視頻流媒體系統中&#xff0c;RTSP&#xff08;Real-Time Streaming Protocol&#xff09;服務模塊通常扮演著“視頻分發中心”的角色&#xff0c;它將編碼后的音視頻內容轉為標準的流媒體格式&#xff0c;供客戶端&#xff08;播放器、云端平臺、AI模塊等&#xff09;拉流…

Nginx發布Vue(ElementPlus),與.NETCore對接(騰訊云)

案例資料鏈接&#xff1a;https://download.csdn.net/download/ly1h1/90745660 1.邏輯說明 1.1 邏輯示意圖 # 前端請求處理邏輯圖瀏覽器請求流程: 1. 瀏覽器發起請求├─ 開發環境(DEV)│ ├─ 請求URL: http://192.168.0.102:3000/api/xxx│ └─ 被Vite代理處理└─ 生產…

解析機器人 2.0.2 | 支持超過50種短視頻平臺的鏈接解析,無水印提取,多功能下載工具

解析機器人是一款功能強大的工具軟件&#xff0c;登錄即可解鎖會員特權。它支持超過50種短視頻平臺的鏈接解析&#xff0c;包括抖音、快手、西瓜、bilibili等&#xff0c;并能實現無水印提取。此外&#xff0c;還提供P2P下載、磁力鏈等多種下載方式&#xff0c;確保用戶能夠快速…

C++ - 數據容器之 forward_list(創建與初始化、元素訪問、容量判斷、元素遍歷、添加元素、刪除元素)

一、創建與初始化 引入 <forward_list> 并使用 std 命名空間 #include <forward_list>using namespace std;創建一個空 forward_list forward_list<int> fl;創建一個包含 5 個元素&#xff0c;每個元素初始化為 0 的 forward_list forward_list<int&g…

Python爬蟲實戰:獲取企信網指定公司基本工商數據并分析,為客戶選擇公司做參考

一、引言 在商業決策、市場調研等眾多領域,企業的基本工商信息是至關重要的參考依據。企信網作為權威的企業信息查詢平臺,匯聚了海量企業的詳細信息。借助 Python 的爬蟲技術,能夠自動從企信網獲取指定公司的工商信息,再運用數據分析和機器學習方法對這些信息進行深入挖掘…

STM32部分:2-1、STM32CubeMX介紹

飛書文檔https://x509p6c8to.feishu.cn/wiki/BTv4wW3O7ita1dkQGkrcBb9rnXg 資料手冊 英文手冊 https://www.stmcu.com.cn/Designresource/detail/user_manual/711316 中文手冊 https://www.stmcu.com.cn/Designresource/detail/localization_document/710583 界面說明 首…

SVM實戰:從理論到鳶尾花數據集的分類可視化

SVM實戰&#xff1a;從理論到鳶尾花數據集的分類可視化 在機器學習的廣闊領域中&#xff0c;支持向量機&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;作為一種經典且強大的分類算法&#xff0c;備受矚目。它憑借獨特的思想和卓越的性能&#xff0c;在模式識…

陶瓷陶器缺陷檢測VOC+YOLO格式938張2類別

數據集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路徑的txt文件&#xff0c;僅僅包含jpg圖片以及對應的VOC格式xml文件和yolo格式txt文件) 圖片數量(jpg文件個數)&#xff1a;938 標注數量(xml文件個數)&#xff1a;938 標注數量(txt文件個數)&#xff1a;938 標注…

通過Docker部署Prometheus + Grafana搭建監控平臺【超詳細版】

文章目錄 前言一、Prometheus、Grafana1.1 Prometheus簡介1.2 Grafana簡介1.3 Prometheus的核心組件1.4 Prometheus優點1.5 Prometheus缺點 二、部署Docker三、主節點部署PrometheusGrafana3.1 部署Prometheus3.2 防火墻開放端口3.3 訪問服務3.4 安裝Grafana3.5 防火墻開放端口…

華為云Flexus+DeepSeek征文|DeepSeek-V3商用服務開通教程

目錄 DeepSeek-V3/R1商用服務開通使用感受 DeepSeek-V3/R1商用服務開通 1、首先需要訪問ModelArts Studio_MaaS_大模型即服務_華為云 2、在網站右上角登陸自己的華為云賬號&#xff0c;如果沒有華為云賬號的話&#xff0c;則需要自己先注冊一個。 3、接著點擊ModelArts Stu…

ubuntu20.04修改默認網卡名稱為eth*

在Ubuntu 20.04.6中&#xff0c;遵循可預測網絡接口設備命名規則&#xff0c;網卡名稱默認可能是以"enp*"、"ens*"等開頭的格式&#xff0c;但是實際使用過程中&#xff0c;某些應用只能讀取eth*的網卡&#xff0c;需要修改。 查看網卡名稱 ip link show …

linux下抓包工具--tcpdump介紹

文章目錄 1. 前言2. 命令介紹3. 常見選項3.1. 接口與基本控制3.2 輸出控制3.3 文件操作3.4 高級調試 4. 過濾表達式4.1 協議類型4.2 方向與地址4.3 邏輯運算符 5. 典型使用場景5.1 網絡故障排查5.2 安全分析與入侵檢測5.3 性能分析與優化 linux下抓包工具--tcpdump介紹 1. 前言…

AI大模型-RAG到底能做些什么?

RAG常見的應用場景&#xff0c;有以下幾個方面&#xff1a; 1.智能客服系統&#xff1a;比如電商領域&#xff0c;對客戶提出的常見問題&#xff0c;進行自動回復。減少人力成本。 2.人力資源管理&#xff1a;一個新的員工&#xff0c;入職一家大型公司&#xff0c;公司中有各…

C++ unordered_set unordered_map

上篇文章我們講解了哈希表的實現&#xff0c;這節嘗試使用哈希表來封裝unordered_set/map 1. unordered_set/map的框架 封裝的過程實際上與set/map類似&#xff0c;在unordered_set/map層傳遞一個仿函數&#xff0c;用于取出key值 由于我們平常使用的都是unordered_set/map&…

REST API、FastAPI與Flask API的對比分析

以下是關于REST API、FastAPI與Flask API的對比分析&#xff0c;涵蓋架構設計、性能表現、開發效率等核心維度&#xff1a; 一、核心定位與架構差異 REST API 本質&#xff1a;一種基于HTTP協議的架構風格&#xff0c;強調資源化操作&#xff08;通過URI定位資源&#xff09;、…

實戰交易策略 篇二十二:情緒流龍頭交易策略

文章目錄 系列文章理論基礎股市的本質資金與情緒題材龍頭股龍頭戰法實戰技法情緒流技術分析擇時實操情緒流龍頭戰法要訣六大步驟九大術法買賣點量化標準系列文章 實戰交易策略 篇一:奧利弗瓦萊士短線交易策略 實戰交易策略 篇二:杰西利弗莫爾股票大作手操盤術策略 實戰交易策…

用VNA進行天線阻抗匹配的實例大圖

比如我這天線&#xff0c;在7Mhz時不諧振&#xff0c;我進行匹配 天線的阻抗很高&#xff0c;大約是在500-1400歐&#xff0c;而等效電容電感很小。 所以我考慮使用阻抗變壓器降低阻抗。 1。測試天線阻抗&#xff0c;電阻相當高&#xff0c;等效電容很小。 2。通過磁環匹配到…