【初階數據結構】樹——二叉樹——堆(中)

文章目錄

前言

一、堆的概念與結構

二、堆的實現

堆的定義

1.初始化堆

2.堆的銷毀

3.堆的插入

3.1向上調整算法

4.堆的判空

5.求有效個數

6.刪除堆頂數據

6.1向下調整算法

7.獲取棧頂數據

三、完整源碼

總結


前言

上篇了解樹和二叉樹相關的概念,這篇學習一種特殊的二叉樹--堆,通過認識堆來實現堆和堆的應用。


一、堆的概念與結構

如果有一個關鍵碼的集合 K = { k0 , k1 , k2 , ..., k n?1 } ,把它的所有元素按完全二叉樹的順序存儲方式存儲,在一個一維數組中,并滿足: K i <= K2? i+1 ( K i >= K2? i+1 且 K i <= K2? i+2 ), i = 0、1、2... ,則稱為小堆(或大堆)。將根結點最大的堆叫做最大堆或大根堆,根結點最小的堆叫做最小堆或小根堆。
小根堆:

大根堆:

堆的特點:

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

從二叉樹的性質討論出

對于具有 n 個結點的完全二叉樹,如果按照從上至下從左至右的數組順序對所有結點從
0 開始編號,則對于序號為 i 的結點有:
1. 若 i>0 , i 位置結點的雙親序號: (i-1)/2 ; i=0 , i 為根結點編號,無雙親結點
2. 若 2i+1<n ,左孩子序號: 2i+1 , 2i+1>=n 否則無左孩子
3. 若 2i+2<n ,右孩子序號: 2i+2 , 2i+2>=n 否則無右孩子

二、堆的實現

堆的定義

由此,我們可知堆的底層是數組來實現的,則堆的結構是順序結構,可寫出堆的結構定義

typedef int HPDatatype;
//堆的結構
typedef struct Heap
{HPDatatype* arr;int size;      //有效數據個數int capacity; //容量
}HP;

1.初始化堆

代碼解析:

先對未開辟空間的數組指針置為空,再對有效個數和容量大小都置為0.

//初始化
void HPIint(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}

2.堆的銷毀

代碼解析:

先判斷堆是否為空,不為空先對數組進行釋放再置為NULL,再對有效個數和容量大小都置為0

注:對堆開辟空間使用完后就要對堆進行銷毀,避免造成空間浪費,因此要對堆進行銷毀

//銷毀
void HPDesTroy(HP* php)
{//判斷堆是否為空,不為空就直接free再置空assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}

3.堆的插入

代碼解析:

堆的插入就是在尾部進行數據插入。先判斷堆是否已滿,堆已滿就進行 realloc 增容并更新capacity。增容后,把插入進來的數據進行重新調整,用 AdjustUp 函數對堆進行調整

//往堆中插入數據(以建小堆為例)
void HPPush(HP* php, HPDatatype x)
{assert(php);if (php->size == php->capacity){//增容int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDatatype* tmp = (HPDatatype*)realloc(php->arr, sizeof(HPDatatype) * newCapacity);if (tmp == NULL){perror("realloc fail!\n");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//插入數據php->arr[php->size] = x;//插入數據后用向上調整方法來調整成小堆或者大堆AdjustUp(php->arr, php->size);++php->size;
}

3.1向上調整算法

接下來給大家介紹AdjustUp 函數?

代碼解析:

先將元素插入到堆的末尾,即最后一個孩子之后
插入之后如果堆的性質遭到破壞,將新插入結點順著其雙雙親往上調整到合適位置即可

由二叉樹的性質,我們可知已知孩子結點可求父結點:Parent =(child-1)/2。在這里我們建小堆,要求孩子結點大于父結點,如果不滿足就對進行交換,在讓child 走到parent ,parent 走到parent 的父結點;如果滿足不用交換直接跳出循環。

//向上調整算法:
void AdjustUp(HPDatatype* arr, int child)
{//已知子節點下標,來求父節點下標int parent = (child - 1) / 2;while (child>0){if (arr[child] > arr[parent])//建大堆就大于{Swap(&arr[child], &arr[parent]);//交換后,子節點會跑到舊位置的父節點,則再求新位置的父節點child = parent;parent = (child - 1) / 2;}else {break;//如果子節點大于父節點,則不需要變化直接跳出循環}}
}

時間復雜度:

因為堆是完全?叉樹,?滿?叉樹也是完全?叉樹,此處為了簡化使?滿?叉樹來證明(時間復雜度本來看的就是近似值,多?個結點不影響最終結果)
分析:
第1層, 2 0 個結點,需要向上移動0層

第2層, 2 1 個結點,需要向上移動1層
第3層, 2 2 個結點,需要向上移動2層
第4層, 2 3 個結點,需要向上移動3層
......
第h層, 2 h ?1 個結點,需要向上移動h-1層
則需要移動結點總的移動步數為:每層結點個數 * 向上調整次數(第?層調整次數為0)

4.堆的判空

代碼解析:用bool函數來判斷堆是否為空

bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}

5.求有效個數

代碼解析:直接返回有效個數即可

int HPSize(HP* php)
{assert(php);return php->size;
}

6.刪除堆頂數據

代碼解析:

先判斷堆是否為空堆,是就返回0,不是返回有效數據;讓根結點和最后一個元素進行交換,把交換后的最后一個元素刪除后,再進行向下調整算法

//刪除堆頂數據(以建小堆為例)
void HPPop(HP* php)
{assert(!HPEmpty(php));//交換根節點和最后一個節點,再對新的最后一個節點進行刪除Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//使用向下調整算法AdjustDown(php->arr, 0, php->size);}

6.1向下調整算法

代碼解析:

將堆頂元素與堆中最后一個元素進行交換
刪除堆中最后一個元素
將堆頂元素向下調整到滿足堆特性為止

由二叉樹的性質,我們可知,已知parent 結點的下標,就可求左,右孩子結點的下標,左下標:2(parent )+1=child, 右下標:2parent +2=child 。把最后一個元素刪除后,這里需要建小堆,先找到孩子結點中較小的結點,把父結點和較小的孩子結點進行交換,再讓父結點走到較小的孩子結點的交換前的位置,再更新孩子結點的下標;如果父結點小于孩子結點就不用交換,直接跳出循環。

//向下調整算法
void AdjustDown(HPDatatype* arr, int parent, int n)
{//已知父節點,求子節點又可求左右子節點int child = parent * 2 + 1;//左子節點while (child<n){//找到最小的節點,讓其與父節點進行交換,誰小誰往上調(如果是大堆,誰大誰往上調)if (child + 1 <n && arr[child] > arr[child + 1]){child++;}if (arr[child] < arr[parent]){Swap(&arr[child], &arr[parent]);//父節點走到舊的字節點下標,再求新子節點下標parent = child;child = parent * 2 + 1;}else {break;}}
}

時間復雜度:

第1層, 2 0 個結點,需要向下移動h-1層
第2層, 2 1 個結點,需要向下移動h-2層
第3層, 2 2 個結點,需要向下移動h-3層
第4層, 2 3 個結點,需要向下移動h-4層
......
第h-1層, 2 h ?2 個結點,需要向下移動1層

則需要移動結點總的移動步數為:每層結點個數 * 向下調整次數

注:堆的向上調整算法和向下調整算法都可以建大堆和小堆。向上調整算法主要用于堆插入,向下調整算法主要用于堆應用和堆排序。通過兩者得出的時間復雜度,可知向下調整算法時間復雜度比向上調整算法復雜度好。

7.獲取棧頂數據

代碼解析:直接返回棧頂的數據

HPDatatype HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}

三、完整源碼

Heap,h:

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>//一、用順序結構實現完全二叉樹,底層是數組
typedef int HPDatatype;
//堆的結構
typedef struct Heap
{HPDatatype* arr;int size;      //有效數據個數int capacity; //容量
}HP;//初始化
void HPIint(HP* php);
//銷毀
void HPDesTroy(HP* php);
//往堆中插入數據
void HPPush(HP* php, HPDatatype x);
//刪除堆頂數據
void HPPop(HP* php);
//判空
bool HPEmpty(HP* php);
//求size
int HPSize(HP* php);
//獲取棧頂數據
HPDatatype HPTop(HP* php);//向上調整算法:
void AdjustUp(HPDatatype* arr, int child);
//向下調整算法
void AdjustDown(HPDatatype* arr, int parent, int n);

Heap,c:

#include"Heap.h"//初始化
void HPIint(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
//銷毀
void HPDesTroy(HP* php)
{assert(php);//判斷堆是否為空,不為空就直接free再置空if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}//交換:父節點與子節點比較,誰小誰往上調(誰大誰往上調)
void Swap(int* x, int* y)
{int temp = *x;*x = *y;*y = temp;
}//向上調整算法:
void AdjustUp(HPDatatype* arr, int child)
{//已知子節點下標,來求父節點下標int parent = (child - 1) / 2;while (child>0){if (arr[child] > arr[parent])//建大堆就大于{Swap(&arr[child], &arr[parent]);//交換后,子節點會跑到舊位置的父節點,則再求新位置的父節點child = parent;parent = (child - 1) / 2;}else {break;//如果子節點大于父節點,則不需要變化直接跳出循環}}
}//往堆中插入數據(以建小堆為例)
void HPPush(HP* php, HPDatatype x)
{assert(php);if (php->size == php->capacity){//增容int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDatatype* tmp = (HPDatatype*)realloc(php->arr, sizeof(HPDatatype) * newCapacity);if (tmp == NULL){perror("realloc fail!\n");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//插入數據php->arr[php->size] = x;//插入數據后用向上調整方法來調整成小堆或者大堆AdjustUp(php->arr, php->size);++php->size;
}///
//對堆進行判斷是否為空
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}
//求size
int HPSize(HP* php)
{assert(php);return php->size;
}//向下調整算法
void AdjustDown(HPDatatype* arr, int parent, int n)
{//已知父節點,求子節點又可求左右子節點int child = parent * 2 + 1;//左子節點while (child<n){//找到最小的節點,讓其與父節點進行交換,誰小誰往上調(如果是大堆,誰大誰往上調)if (child + 1 <n && arr[child] > arr[child + 1]){child++;}if (arr[child] < arr[parent]){Swap(&arr[child], &arr[parent]);//父節點走到舊的字節點下標,再求新子節點下標parent = child;child = parent * 2 + 1;}else {break;}}
}//刪除堆頂數據(以建小堆為例)
void HPPop(HP* php)
{assert(!HPEmpty(php));//交換根節點和最后一個節點,再對新的最后一個節點進行刪除Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//使用向下調整算法AdjustDown(php->arr, 0, php->size);}//獲取棧頂數據
HPDatatype HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}

test,c:

#include"Heap.h"void test()
{HP hp;HPIint(&hp);HPPush(&hp, 6);HPPush(&hp, 4);HPPush(&hp, 8);HPPush(&hp, 10);//HPPop(&hp);//HPPop(&hp);//HPPop(&hp);//HPPop(&hp);while (!HPEmpty(&hp)){printf("%d ", HPTop(&hp));HPPop(&hp);//取棧頂就要刪除棧頂,不然會一直循環}HPDesTroy(&hp);
}


總結

非常感謝大家閱讀完這篇博客。希望這篇文章能夠為您帶來一些有價值的信息和啟示。如果您發現有問題或者有建議,歡迎在評論區留言,我們一起交流學習。

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

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

相關文章

AM剪輯軟件漢化版:簡單易用,開啟視頻創作之旅

在短視頻流量時代&#xff0c;視頻剪輯已經成為許多人表達自我和記錄生活的重要方式。無論是分享日常點滴、制作創意視頻還是進行專業內容創作&#xff0c;一款功能強大且操作簡便的視頻剪輯軟件是必不可少的。今天&#xff0c;我們要介紹的 AM剪輯軟件漢化版&#xff0c;就是這…

llfc項目分布式服務筆記

一、系統整體架構流程圖(簡明版) 復制代碼 +---------------+ +------------------+ +----------------+ | 客戶端 (Client) |--------->| GateServer |----------| StatusServer |<--+ +---------------+ +--------------…

C++如何設計和實現緩存(cache)來減少對后端存儲的訪問壓力

隨著數據量的激增和用戶對低延遲、高吞吐量需求的不斷提升,如何減少系統瓶頸、提升響應速度成為了開發者的核心挑戰之一。在這一背景下,緩存(cache)作為一種關鍵的技術手段,逐漸成為解決性能問題的核心策略。緩存的本質是通過存儲頻繁訪問的數據或計算結果,減少對后端存儲…

華為設備端口隔離

端口隔離的理論與配置指南 一、端口隔離的理論 基本概念 端口隔離&#xff08;Port Isolation&#xff09;是一種在交換機上實現的安全功能&#xff0c;用于限制同一VLAN內指定端口間的二層通信。被隔離的端口之間無法直接通信&#xff0c;但可通過上行端口訪問公共資源&#…

1688平臺商品詳情接口開發指南(含Python代碼示例)

接口概述 1688開放平臺提供的商品詳情接口&#xff08;item_get&#xff09;是獲取商品核心數據的重要API&#xff0c;開發者可通過該接口獲取商品標題、價格、規格參數、圖片等詳細信息。本文重點解析標題字段的獲取方式&#xff0c;并提供完整代碼示例。 接口請求參數 基礎…

Edge瀏覽器PDF字體顯示錯誤

Edge瀏覽器PDF字體顯示錯誤 軟件版本信息 Edge Version: 136.0.3240.50 Word Version: Microsoft Office 專業增強版2021問題描述 在Word中使用多級列表自動編號, 并使用Word軟件自帶的導出為PDF文件功能, 在Word中顯示正常的數字, 在Edge中查看PDF將會出現渲染錯誤的現象,…

Redis能保證數據不丟失嗎之AOF

我們都知道,Redis是一個基于內存的k-v數據庫,既然是基于內存的,那么Redis如何保證數據不丟失?以及真的能做到數據的百分百不丟失嗎? 為什么Redis數據需要持久化機制? Redis的一個常用場景是緩存,通常緩存丟失的話,我們也可以從數據庫中重新找回,那么為什么Redis還需…

Apache POI實現Excel的基本寫入、導出操作

目錄 一、Apache POI 簡介 二、入門案例(寫入導出) 三、實際開發過程中的導出操作——&#xff08;將文件下載至客戶端瀏覽器中&#xff09; 一、Apache POI 簡介 Apache POI&#xff08;Poor Obfuscation Implementation&#xff09;是 Apache 軟件基金會的開源項目&#…

HTTP請求與前端資源未優化的系統性風險與高性能優化方案

目錄 前言一、未合并靜態資源&#xff1a;HTTP請求的隱形殺手1.1 多文件拆分的代價1.2 合并策略與工具鏈實踐 二、未啟用GZIP壓縮&#xff1a;傳輸流量的浪費2.1 文本資源的壓縮潛力2.2 服務端配置與壓縮算法選擇 三、未配置瀏覽器緩存&#xff1a;重復請求的根源3.1 緩存失效的…

AgentMesh開源多智能體 (Multi-Agent) 平臺

AgentMesh 是一個開源的多智能體 (Multi-Agent) 平臺&#xff0c;核心目標是解決多個智能體之間的通信和協同問題&#xff0c;真正實現 “11>2” 的效果。能夠幫助用戶快速創造自己的多智能體團隊&#xff0c;或是讓已有的多個單一智能體獲得協同能力&#xff0c;最終解決更…

基于Jetson Nano與PyTorch的無人機實時目標跟蹤系統搭建指南

引言&#xff1a;邊緣計算賦能智能監控 在AIoT時代&#xff0c;將深度學習模型部署到嵌入式設備已成為行業剛需。本文將手把手指導讀者在NVIDIA Jetson Nano&#xff08;4GB版本&#xff09;開發板上&#xff0c;構建基于YOLOv5SORT算法的實時目標跟蹤系統&#xff0c;集成無人…

從入門到登峰-嵌入式Tracker定位算法全景之旅 Part 8 |產品化與運維:批量標定、誤差監控、OTA 升級與安全防護

Part 8 |產品化與運維:批量標定、誤差監控、OTA 升級與安全防護 本章聚焦將嵌入式 Tracker 定位系統推向 量產與運維 階段,覆蓋 批量標定、誤差監控、遠程 OTA 升級 以及 定位安全防護,確保產品在大規模部署后仍能穩定、精準、可靠地運行。 一、批量標定平臺搭建 標定流程…

gsplat 渲染庫 安裝部署筆記

目錄 Windows 安裝 Nvdiffrast安裝 gsplat安裝成功筆記: cu118測試ok vs 編譯安裝報錯: 安裝命令: 報錯結果: Windows 安裝 pip install gsplat 安裝成功,調用報錯: python -c "from gsplat import csrc as _C" Traceback (most recent call last): …

Java二維碼學習

使用Java語言生成二維碼有以下方式,一是谷歌的zxing,二是基于zxing實現的qrcode開源項目,三是基于zxing實現的qrgen開源項目 一 zxing 谷歌的zxing技術生成二維碼,是MultiFormatWriter多寫格式書寫器生成BitMatrix位矩陣,然后將位矩陣的信息在BufferedImage中設置二維碼…

工業質檢/缺陷檢測領域最新頂會期刊論文收集整理 | AAAI 2025【持續更新中】

會議官方論文列表&#xff1a;https://ojs.aaai.org/index.php/AAAI/issue/view/624 其中&#xff0c;2025年是第三十九屆AAAI人工智能大會&#xff0c;主要對第三十九屆相關論文進行梳理&#xff0c;當前已初版28期(volume 39 no. 28) 【Attention】 雖然本文主要面向的領域…

數據結構實驗8.1:圖的基本操作

文章目錄 一&#xff0c;實驗目的二&#xff0c;實驗內容三&#xff0c;實驗要求四&#xff0c;算法分析五&#xff0c;示例代碼8-1.cpp源碼graph.h源碼 六&#xff0c;操作步驟七&#xff0c;運行結果 一&#xff0c;實驗目的 1&#xff0e;掌握圖的鄰接矩陣、鄰接表的表示方…

Spring Boot3 實現定時任務 每10分鐘執行一次,同時要解決分布式的問題 區分不同場景

在Spring Boot 3中實現分布式定時任務&#xff0c;確保多實例環境下任務僅執行一次&#xff0c;可以采用以下方案&#xff1a; 方案一&#xff1a;Redis分布式鎖&#xff08;推薦&#xff09; import org.springframework.data.redis.core.StringRedisTemplate; import org.sp…

WPF MVVM入門系列教程(五、命令和用戶輸入)

&#x1f9ed; WPF MVVM入門系列教程 一、MVVM模式介紹二、依賴屬性三、數據綁定四、ViewModel五、命令和用戶輸入六、ViewModel案例演示 WPF中的命令模型 在WPF中&#xff0c;我們可以使用事件來響應鼠標和鍵盤動作。 但使用事件會具備一定的局限性&#xff0c;例如&#x…

2025年01月09日德美醫療前端面試

目錄 vue2 的雙向綁定的原理vue3 的雙向綁定原理vue 的生命周期vue 子組件為何不能修改父組件的值js delete 刪除數組的某一個值會怎么樣vue 和 react 的 diff 算法什么是閉包原型鏈this指向 vue2 的雙向綁定的原理 以下是 Vue 2 雙向綁定的原理&#xff1a; 1. 核心概念 …

知識圖譜 + 大語言模型:打造更聰明、更可靠的AI大腦 —— 探索 GraphRAG 中文優化與可視化實踐

大語言模型&#xff08;LLMs&#xff09;無疑是近年來人工智能領域最耀眼的明星。它們強大的自然語言理解和生成能力&#xff0c;在文本創作、代碼生成、對話交互等眾多領域展現了驚人的潛力。然而&#xff0c;當前的 LLMs 并非完美無缺&#xff0c;它們常常面臨著“幻覺”&…