【初探數據結構】二叉樹的順序結構——堆的實現詳解(上下調整算法的時間復雜度分析)

💬 歡迎討論:在閱讀過程中有任何疑問,歡迎在評論區留言,我們一起交流學習!
👍 點贊、收藏與分享:如果你覺得這篇文章對你有幫助,記得點贊、收藏,并分享給更多對數據結構感興趣的朋友

文章目錄

前言

堆是一種基于完全二叉樹的數據結構,通常分為最大堆(父節點值≥子節點)和最小堆(父節點值≤子節點)。由于完全二叉樹的特性,堆可以用數組高效存儲,通過索引關系快速定位父子節點。


1. 堆的概念與結構

如果有?個關鍵碼的集合,把它的所有元素按完全?叉樹的順序存儲?式存儲,在?個?維數組中,并滿?: K = { k 0 , k 1 , k 2 , . . . , k n ? 1 } K=\{ k_0,k_1,k_2,...,k_{n-1} \} K={k0?,k1?,k2?,...,kn?1?}i = 0、1、2...,則稱為?堆(或?堆)。將根結點最?的堆叫做最?堆或?根堆,根結點最?的堆叫做最?堆或?根堆。

在這里插入圖片描述


1.2 堆的重要性質

對于具有n個結點的完全二叉樹,如果按照從上至下、從左至右的數組順序對所有結點從0開始編號,對于序號為i的結點有:

  1. i > 0i位置結點的雙親序號為:(i - 1)/2; 當i0時,i為根結點。
  2. 2i + 1 < n,左孩子序號:2i + 1;如果2i + 1 >=n,則無左孩子
  3. 2i + 2 < n,左孩子序號:2i + 2;如果2i + 2 >=n,則無右孩子

2. 堆的實現

原碼自取gitee

現在我們知道,完全二叉樹的順序結構就是堆,顧名思義,堆就是用順序表(數組)實現的。
在這里插入圖片描述
Heap.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<time.h>typedef int HPDataType;
typedef struct Heap
{HPDataType* a;//堆int size;//堆的大小int capacity;//堆的容量
}HP;
//堆的初始化
void HeapInit(HP* php);
// 堆的銷毀
void HeapDestory(HP* php);
// 堆的插入
void HeapPush(HP* php, HPDataType x);
// 堆的刪除
void HeapPop(HP* php);
// 取堆頂的數據
HPDataType HeapTop(HP* php);
// 堆的數據個數
int HeapSize(HP* php);
// 堆的判空
int HeapEmpty(HP* php);
//向下調整
void AdjustDown(HPDataType* a, int n, int parent);
//向上調整
void AdjustUp(HPDataType* a, int child);
//交換
void swap(HPDataType* p1, HPDataType* p2);

聲明:本文以實現小堆為例,如需實現大堆,修改小堆調整算法的條件即可,這里不過多贅述。


2.1 堆的插入+向上調整算法

2.1.1 向上調整算法

該算法輔助實現堆的插入

將新數據插入到數組的尾上,再進行向上調整算法,直到滿足堆。

在這里插入圖片描述
仔細觀察這個圖解,不難發現:從某個結點出發,與其父親比較,如果小于他的父親,就交換位置;如果大于或等于就不動,調整結束。

void AdjustUp(HPDataType* a,int child)
{int parent = (child - 1) / 2;while (child > 0) {if (a[child] > a[parent]) {swap(&a[child],&a[parent]);child = parent;parent = (child - 1) / 2;}else {break;}}
}

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

在這里插入圖片描述

void HeapPush(HP* php, HPDataType x)
{assert(php);//若空間不足,則擴容if (php->size == php->capacity) {HPDataType* ptr = realloc(php->a, sizeof(HPDataType) * php->capacity * 2);if (ptr == NULL) {perror("realloc fail");exit(-1);}php->a = ptr;php->capacity *= 2;}php->a[php->size] = x;AdjustUp(php->a, php->size);php->size++;
}

2.2 堆的刪除+向下調整算法

2.2.1 向下調整算法

該算法輔助實現堆的刪除

現在我們給出一個數組,邏輯上看做一顆完全二叉樹。我們通過從根節點開始的向下調整算法可以把它調整成一個小堆。向下調整算法有一個前提左右子樹必須是一個堆,才能調整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

向下調整過程圖
仔細觀察這個圖解,不難發現:從根結點出發,與其較小的孩子比較,如果小于這個孩子,就交換位置;如果大于或等于就不動,調整結束。

代碼核心:

  1. 父親與孩子的下標關系:child=2*parent + 1
  2. 孩子的下標小于n,避免數組越界訪問
void AdjustDown(HPDataType* a,int n,int parent)
{int child = 2 * parent + 1;while (child < n) {// 假設法,選出左右孩?中?的那個孩?if (a[child] < a[child + 1] && child + 1 < n){child++;}if (a[parent] < a[child]) {swap(&a[parent], &a[child]);parent = child;child = 2 * parent + 1;}else break;}
}

2.2.2 堆的刪除

刪除時需注意判斷堆是否為非空,若對空堆進行刪除,必然出錯。

  • 將堆頂元素與堆中最后?個元素進?交換
  • 刪除堆中最后?個元素
  • 將堆頂元素向下調整到滿?堆特性為?
    在這里插入圖片描述
void HeapPop(HP* php)
{assert(php);assert(!HeapEmpty(php));swap(php->a,&php->a[php->size-1]);php->size--;AdjustDown(php->a,php->size, 0);
}

其余的初始化、銷毀、判空等等,非常簡單,都是老套路了,這里留給讀者自己發揮。
在我過去的一些文章都有類似的詳解(順序表、鏈表、棧、隊列)。大家可以照貓畫虎。

傳送門呈上~
【初探數據結構】線性表———順序表的詳解和實現
【初探數據結構】線性表————鏈表(一)(單鏈表的實現)
【初探數據結構】線性表——鏈表(二)帶頭雙向循環鏈表(詳解與實現)
【初探數據結構】線性表——棧與隊列(代碼實現與詳解)


3. 復雜度分析(向上調整與向下調整)

因為堆是完全二叉樹,而滿二叉樹也是完全二叉樹,為了簡化證明,此出以滿二叉樹為研究對象。(時間復雜度本來看的就是近似值,多幾個節點不影響最終結果)


3.1 向下調整時間復雜度( O ( N ) O(N) ON

最壞的情況是什么呢?
就是樹的每個結點全部都要進行一次調整堆底。
設樹的高度為h
我們需要計算的是向下移動的總次數T(n)
在這里插入圖片描述
分析:
第1層, 2 0 2^0 20個結點,需要向下移動h-1
第2層, 2 1 2^1 21個結點,需要向下移動h-2
第3層, 2 2 2^2 22個結點,需要向下移動h-3
第4層, 2 3 2^3 23個結點,需要向下移動h-4

第h-1層, 2 h ? 2 2^{h-2} 2h?2 個結點,需要向下移動1

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

T ( h ) = 2 0 ? ( h ? 1 ) + 2 1 ? ( h ? 2 ) + 2 2 ? ( h ? 3 ) + 2 3 ? ( h ? 4 ) + … … + 2 h ? 3 ? 2 + 2 h ? 2 ? 1 T(h) = 2^0*(h-1)+ 2^1*(h-2)+2^2*(h-3)+2^3*(h-4)+……+2^{h-3}*2+2^{h-2}*1 T(h)=20?(h?1)+21?(h?2)+22?(h?3)+23?(h?4)+……+2h?3?2+2h?2?1

利用數列的錯位相減(計算步驟如下圖)
在這里插入圖片描述
得出:
T ( h ) = 2 h ? 1 ? h T(h) = 2^h ?1 ? h T(h)=2h?1?h
根據?叉樹的性質:
n = 2 h ? 1 和 h = l o g 2 ( n + 1 ) n = 2^h-1和h=log_2(n+1) n=2h?1h=log2?(n+1)

得出:
T ( n ) = n ? l o g 2 ( n + 1 ) ≈ n T(n)=n-log_2(n+1)≈n T(n)=n?log2?(n+1)n

向下調整算法建堆時間復雜度為:O(n)


3.2 向上調整時間復雜度( n ? l o g n n*logn n?logn)

與向下調整類似,計算所有結點向上移動至堆頂的移動總次數即可
設樹的高度為h
我們需要計算的是向上移動的總次數T(n)

分析:
第1層, 2 0 2^0 20個結點,需要向下移動0
第2層, 2 1 2^1 21個結點,需要向下移動1
第3層, 2 2 2^2 22個結點,需要向下移動2
第4層, 2 3 2^3 23個結點,需要向下移動3

第h層, 2 h ? 2 2^{h-2} 2h?2 個結點,需要向下移動h-1

則需要移動結點總的移動步數為:每層結點個數乘向上調整次數(第?層調整次數為0)

T ( h ) = 2 1 ? 1 + 2 2 ? 2 + 2 3 ? 3 + … … + 2 h ? 2 ? ( h ? 2 ) + 2 h ? 1 ? ( h ? 1 ) T(h) = 2^1*1+2^2*2+2^3*3+……+2^{h-2}*(h-2)+2^{h-1}*(h-1) T(h)=21?1+22?2+23?3+……+2h?2?h?2+2h?1?(h?1)

也是利用數列的錯位相減得:
T ( h ) = ? ( 2 h ? 1 ) + 2 h ? ( h ? 1 ) + 2 0 T(h) = -(2^h-1)+2^h * (h-1)+2^0 T(h)=?(2h?1)+2h?h?1+20
根據?叉樹的性質:
n = 2 h ? 1 和 h = l o g 2 ( n + 1 ) n = 2^h-1和h=log_2(n+1) n=2h?1h=log2?(n+1)

得出:
T ( n ) = ( n + 1 ) ( l o g 2 ( n + 1 ) ? 2 ) + 2 ≈ n ? l o g 2 n T(n)=(n + 1)(log_2(n +1) ? 2) + 2≈n*log_2n T(n)=(n+1)(log2?(n+1)?2)+2n?log2?n

向上調整算法建堆時間復雜度為: n ? l o g n n*logn n?logn


3.3 結論

由此可見,向下調整的效率是優于向上調整的,所以我們以后需要建堆時,應該優先考慮向下調整建堆。再后面的堆排序中我們可以深刻體會到。


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

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

相關文章

流量分析2

一&#xff0c;webshell流量 [GKCTF 2021]簽到 先看協議分級&#xff0c;大部分是tcp&#xff0c;里面有http的基于的行文本數據占了很大的比重&#xff0c;看看里面有什么 過濾http的流量 點擊一條流量&#xff0c;里面的內容進去后面有基于行的文本數據&#xff0c; 先解he…

頭歌實踐教學平臺--【數據庫概論】--SQL

一、表結構與完整性約束的修改(ALTER) 1.修改表名 USE TestDb1; alter table your_table rename TO my_table; 2.添加與刪除字段 #語句1&#xff1a;刪除表orderDetail中的列orderDate alter table orderDetail drop orderDate; #語句2&#xff1a;添加列unitPrice alter t…

在 React 中,組件之間傳遞變量的常見方法

目錄 1. **通過 Props 傳遞數據**2. **通過回調函數傳遞數據**3. **通過 Context API 傳遞數據**4. **通過 Redux 管理全局狀態**5. **通過事件總線&#xff08;如 Node.js 的 EventEmitter&#xff09;**6. **通過 Local Storage / Session Storage**7. **通過 URL 查詢參數傳…

Redis + 布隆過濾器解決緩存穿透問題

Redis 布隆過濾器解決緩存穿透問題 1. Redis 布隆過濾器解決緩存穿透問題 &#x1f4cc; 什么是緩存穿透&#xff1f; 緩存穿透指的是查詢的數據既不在緩存&#xff0c;也不在數據庫&#xff0c;導致每次查詢都直接訪問數據庫&#xff0c;增加數據庫壓力。 例如&#xff1…

Vue動態添加或刪除DOM元素:購物車實例

Vue 指令系列文章: 《Vue插值:雙大括號標簽、v-text、v-html、v-bind 指令》 《Vue指令:v-cloak、v-once、v-pre 指令》 《Vue條件判斷:v-if、v-else、v-else-if、v-show 指令》 《Vue循環遍歷:v-for 指令》 《Vue事件處理:v-on 指令》 《Vue表單元素綁定:v-model 指令》…

vue h5實現車牌號輸入框

哈嘍&#xff0c;大家好&#xff0c;最近鵬仔開發的項目是學校校內車輛超速方面的統計檢測方面的系統&#xff0c;在開發過程中發現有個小功能&#xff0c;就是用戶移動端添加車牌號&#xff0c;剛開始想著就一個輸入框&#xff0c;提交時正則效驗一下格式就行&#xff0c;最后…

硬件基礎(5):(3)二極管的應用

文章目錄 [toc]1. **整流電路****功能**&#xff1a;**工作原理**&#xff1a;**應用實例**&#xff1a;電路組成&#xff1a;整流過程&#xff1a;電路的應用&#xff1a; 2. **穩壓電路****功能**&#xff1a;**工作原理**&#xff1a;**應用實例**&#xff1a;電路組成及功能…

Wireshark網絡抓包分析使用詳解

序言 之前學計網還有前幾天備考華為 ICT 網絡賽道時都有了解認識 Wireshark&#xff0c;但一直沒怎么專門去用過&#xff0c;也沒去系統學習過&#xff0c;就想趁著備考的網絡相關知識還沒忘光&#xff0c;先來系統學下整理點筆記~ 什么是抓包&#xff1f;抓包就是將網絡傳輸…

安心聯車輛管理平臺源碼價值分析

安心聯車輛管理平臺源碼的價值可從技術特性、功能覆蓋、市場適配性、擴展潛力及商業化支持等多個維度進行分析。以下結合實際應用進行詳細解讀&#xff1a; 一、技術架構與開發優勢 主流技術棧與高性能架構 源碼采用成熟的前后端分離架構&#xff0c;后端基于Java技術&#xff…

【操作系統】Docker如何使用-續

文章目錄 1、概述2、鞏固知識2.1、基礎命令2.2、容器管理2.3、鏡像管理2.4、網絡管理2.5、Compose 3、常用命令 1、概述 在使用Docker的過程中&#xff0c;掌握常用的命令是至關重要的。然而&#xff0c;隨著時間的推移&#xff0c;我們可能會遺忘一些關鍵的命令或對其用法變得…

ElementUI el-menu導航開啟vue-router模式

有沒有小伙伴遇到這么一種情況&#xff1a;ElementUI el-menu導航中&#xff0c;開啟vue-router 的模式后&#xff0c;點擊觸發事件而不進行路由跳轉&#xff1f; 別慌&#xff01;下面直接說解決方案&#xff1a; 借助路由守衛進行判斷 給el-menu綁定切換事件&#xff0c;給…

【leetcode hot 100 17】電話號碼的字母組合

分析&#xff1a;當設計關鍵字“所有組合”時&#xff0c;要考慮深度優先遍歷、廣度優先遍歷&#xff08;層次遍歷&#xff09;&#xff0c;其中&#xff1a; 深度優先搜索&#xff1a; 自頂向下的遞歸實現深搜定義子問題在當前遞歸層結合子問題結果解決原問題 廣度優先搜索 利…

Vue 2 探秘:visible 和 append-to-body 是誰的小秘密?

&#x1f680; Vue 2 探秘&#xff1a;visible 和 append-to-body 是誰的小秘密&#xff1f;&#x1f914; 父組件&#xff1a;identify-list.vue子組件&#xff1a;fake-clue-list.vue 嘿&#xff0c;各位前端探險家&#xff01;&#x1f44b; 今天我們要在 Vue 2 的代碼叢林…

C++學習之路:從頭搞懂配置VScode開發環境的邏輯與步驟

目錄 編輯器與IDE基于vscode的C開發環境配置1. 下載vscode、淺嘗編譯。番外篇 2. 安裝插件&#xff0c;賦能編程。3. 各種json文件的作用。c_cpp_properties.jsontask.jsonlaunch.json 總結&&彩蛋 編輯器與IDE 上一篇博客已經介紹過了C程序的一個編譯流程&#xff0c;從…

PPT 轉高精度圖片 API 接口

PPT 轉高精度圖片 API 接口 文件處理 / 圖片處理&#xff0c;將 PPT 文件轉換為圖片序列。 1. 產品功能 支持將 PPT 文件轉換為高質量圖片序列&#xff1b;支持 .ppt 和 .pptx 格式&#xff1b;保持原始 PPT 的布局和樣式&#xff1b;轉換后的圖片支持永久訪問&#xff1b;全…

VSCode 抽風之 兩個conda環境同時在被激活

出現了神奇的(toolsZCH)(base) 提示符&#xff0c;如下圖所示&#xff1a; 原因大概是&#xff1a;conda 環境的雙重激活&#xff1a;可能是 conda 環境沒有被正確清理或初始化&#xff0c;導致 base 和 toolsZCH 同時被激活。 解決辦法就是 &#xff1a;conda deactivate 兩次…

git | 回退版本 并保存當前修改到stash,在進行整合。[git checkout | git stash 等方法 ]

目錄 一些常見命令&#xff1a; git 回退版本 一、臨時回退&#xff08;不會修改歷史&#xff0c;可隨時回到當前版本&#xff09; 方法1&#xff1a;git checkout HEAD~1 問題&#xff1a;處于 detached HEAD 狀態下提交的&#xff0c;無法直接 git push ? 選項 1&…

如何使用 Postman 進行接口測試?

使用 Postman 這一工具&#xff0c;可以輕松地進行接口測試。以下是一份簡單的使用教程&#xff0c;幫助你快速上手。 Postman 接口測試教程&#xff1a;詳細步驟及操作技巧

寫作軟件新體驗:讓文字創作更高效

一、開篇引入:寫作難題的破解之道 在當今信息爆炸的時代,寫作成為了我們生活和工作中不可或缺的一部分。然而,面對繁瑣的寫作任務,我們時常感到力不從心,甚至陷入創作的瓶頸。那么,有沒有一款軟件能夠幫助我們破解這一難題,讓文字創作變得更加高效和輕松呢?答案是肯定…

大模型思維鏈COT:Chain-of-Thought Prompting Elicits Reasoningin Large Language Models

一、TL&#xff1b;DR 探索了COT&#xff08;chain-of-thought prompting&#xff09;通過一系列的中間推理步驟來顯著的提升了LLM的復雜推理能力在三個大型語言模型上的實驗表明&#xff0c;思維鏈提示能夠提升模型在一系列算術、常識和符號推理任務上的表現解釋了一下為什么…