【數據結構】揭秘二叉樹與堆--用C語言實現堆

文章目錄

  • 1.樹
    • 1.1.樹的概念
    • 1.2.樹的結構
    • 1.3.樹的相關術語
  • 2.二叉樹
    • 2.1.二叉樹的概念
    • 2.2.特殊的二叉樹
    • 2.2.1.滿二叉樹
    • 2.2.2.完全二叉樹
    • 2.3.二叉樹的特性
    • 2.4.二叉樹的存儲結構
      • 2.4.1.順序結構
      • 2.4.2.鏈式結構
  • 3.堆
    • 3.1.堆的概念
    • 3.2.堆的實現
      • 3.2.1.堆結構的定義
      • 3.2.2.堆的初始化
      • 3.2.3.堆的銷毀
      • 3.2.4.插入數據
        • 3.2.4.1.向上(下)調整算法
        • 3.2.4.2.插入數據
      • 3.2.5.堆的判空
      • 3.2.6.刪除(堆頂)數據
      • 3.2.7.取堆頂數據
      • 3.2.8.堆的數據個數
    • 3.3.完整代碼
    • Heap.h
    • Heap.c
    • main.c
    • 3.4.運行結果

1.樹

1.1.樹的概念

樹是一種非線性的數據結構,是由n(n>=0)個有限結點組成的具有層次關系的集合

  • 樹的結構類似于一棵倒掛的樹(根在上,枝葉在下)
  • 根節點是一個特殊的結點,它沒有前驅結點
  • 除去根結點,其余結點又被分成了m(m>=0)個集合,這些集合被稱為根結點的子樹
  • 每個子樹又是一個與樹相似的結構,都只有一個前驅,有0或多個后繼,因此樹是遞歸定義的

1.2.樹的結構

在樹形結構中,子樹之間不能有交集,否則就是非樹形結構
非樹形結構:
請添加圖片描述

1.3.樹的相關術語

請添加圖片描述
父結點/雙親結點:若一個結點含有前驅結點,則這個前驅結點就是該結點的父結點,如圖:A是B、C、D的父結點,B是E、F的父結點···
子結點/孩子結點:若一個結點有后繼節點,那么這個后繼結點就是該結點的子結點,如圖:B是A的子結點,G是D的子結點···
結點的度:一個結點含有的子結點個數就是該結點的度,如圖:A的度為3,C的度為0···
樹的度:一棵樹中,我們把所有結點中最大的度,稱為樹的度,如圖:度為3(A和D的度最大)
葉?結點/終端結點:度為0的結點,如圖:J、K、L、M···
分?結點/?終端結點:度不為 0 的結點,如圖:B、C、D、E、F···
兄弟結點:具有相同?結點的結點互稱為兄弟結點(親兄弟),如圖:B、C 是兄弟結點
結點的層次:從根開始定義起,根為第 1 層,根的?結點為第 2 層,以此類推
樹的?度或深度:樹中結點的最?層次,如圖:高度為4
結點的祖先:從根到該結點所經分?上的所有結點,如圖: A是所有結點的祖先
路徑:?條從樹中任意節點出發,沿?結點-?結點連接,達到任意節點的序列,?如A到O的路徑為:
A-D-I-O
?孫:以某結點為根的?樹中任?結點都稱為該結點的?孫,如圖:所有結點都是A的?孫
森林:由 m(m>0) 棵互不相交的樹的集合稱為森林

2.二叉樹

2.1.二叉樹的概念

二叉樹是最常見的樹形結構結構,通常由一個根節和兩課子樹構成
請添加圖片描述

  1. 二叉樹中,每個結點的度都不大于2
  2. 二叉樹是有序樹,左右子樹有次序之分,不能顛倒

2.2.特殊的二叉樹

2.2.1.滿二叉樹

若一個二叉樹中,每一層結點數都達到了最大值,那么這棵樹就是滿二叉樹,假設層數為k,那么總結點個數為2k?12^k-12k?1
請添加圖片描述

2.2.2.完全二叉樹

完全二叉樹是由滿二叉樹得來的,最后一層的結點個數不一定達到最大,其他層結點個數都到達最大值,且結點從左到右排列

請添加圖片描述

2.3.二叉樹的特性

規定根結點的層數為1:

  1. 二叉樹第i層上最多有2i?12^{i-1}2i?1個結點
  2. 深度為k的二叉樹,最多有2k2^k2k個結點
  3. 結點個數為n的滿二叉樹,它的深度為log2(n+1)log_{2}(n+1)log2?(n+1)

2.4.二叉樹的存儲結構

一般分為順序存儲和鏈式存儲兩種

2.4.1.順序結構

即用數組(順序表)按順序存儲每一個節點
請添加圖片描述

2.4.2.鏈式結構

用鏈表來表示二叉樹,每一個節點都包含兩個指針,分別指向自己的左孩子結點和右孩子結點,若該節點沒有對應的孩子結點,則對應的指針為空

3.堆

3.1.堆的概念

大根堆/大堆/最大堆:把數據按照順序存儲的方式存儲到一個完全二叉樹中,其中根結點最大,每個結點的左右結點都不大于它本身,這樣的存儲結構就叫大根堆
小根堆/小堆/最小堆:把數據按照順序存儲的方式存儲到一個完全二叉樹中,其中根結點最小,每個結點的左右結點都不小于它本身,這樣的存儲結構就叫小根堆

假設根節點序號為0,節點總數為n(n>0),取任意結點序號為i,則
左孩子序號=2i+1(<n),若結果大于等于n,則該結點沒有左孩子
右孩子序號=2
i+2(<n),若結果大于等于n,則該結點沒有右孩子

3.2.堆的實現

3.2.1.堆結構的定義

由于堆是按照順序存儲方式存儲的,所以結構體中的成員要包含一個數組(指針)、有效數據個數、數組的空間大小

//堆的結構
typedef int HPDataType;
struct Heap{HPDataType* arr;int size;//有效數據個數int capacity;//空間大小
}HP;

3.2.2.堆的初始化

把所有成員置為空即可

void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}

3.2.3.堆的銷毀

釋放數組,把成員都置為空即可

void HPDestroy(HP* php)
{assert(php);free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}

3.2.4.插入數據

3.2.4.1.向上(下)調整算法

由于插入數據后,會破壞堆的平衡,因此我們要創建一個函數,用來調整堆中的數據位置,使堆再次保持平衡

向上調整算法:以創建小堆為例,從當前節點開始,與它的父節點比較,如果它小于父節點,那么與父節點交換位置,繼續從他的父節點重復該操作,直到遇到根節點或當前節點不小于父節點為止
交換數據函數:

void swap(HPDataType* a, HPDataType* b)
{HPDataType tmp = *a;*a = *b;*b = tmp;
}

向上調整算法:

//logn
void AjustUp(HPDataType* arr, int child)
{//一直向上比較并調整 直到遇到根節點while(child > 0){//計算父節點int parent = (child - 1)/2;//比較//建小堆 <//建大堆 >if(arr[child] < arr[parent]){swap(&arr[child], &arr[parent]);child = parent;}else{break;}}
}

向下調整算法:以創建小堆為例,從根節點開始,與左右孩子中最小的比較,若根節點大于它,則跟它交換位置,并從該孩子節點繼續重復以上操作,直到當前節點下標超出節點總數或左右孩子結點都不小于它為止

//logn
void AJustDown(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;}}
}
3.2.4.2.插入數據

在數組末尾插入數據,再用向上調整算法使堆平衡即可

void HPPush(HP* php, HPDataType x)
{assert(php);//空間不夠 擴容if(php->capacity == php->size){int newCapacity = php->capacity==0 ? 4 : 2*php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->arr, sizeof(HPDataType)*newCapacity);//擴容失敗if(tmp == NULL){perror("Realoc Failed!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}php->arr[php->size++] = x;//向上調整AjustUp(php->arr, php->size-1);
}

3.2.5.堆的判空

判斷size是否等于0即可

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

3.2.6.刪除(堆頂)數據

先判斷堆是否為空,若不為空,交換堆頂數據與數組末尾數據的位置,size–,再用向下調整算法使堆平衡即可

void HPPop(HP* php)
{assert(!HPEmpty(php));swap(&php->arr[0], &php->arr[php->size-1]);--php->size;AJustDown(php->arr, 0, php->size);
}

3.2.7.取堆頂數據

若堆不為空,返回數組第一個元素即可

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

3.2.8.堆的數據個數

返回size值即可

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

3.3.完整代碼

Heap.h

//
//  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 HPInit(HP* php);
//銷毀
void HPDestroy(HP* php);//交換
void swap(HPDataType* a, HPDataType* b);
//向上調整算法
void AjustUp(HPDataType* arr, int child);
//向上調整算法
void AJustDown(HPDataType* arr, int child, int n);//插入數據
void HPPush(HP* php, HPDataType x);//判空
bool HPEmpty(HP* php);//刪除數據
void HPPop(HP* php);//取堆頂元素
HPDataType HPTop(HP* php);//數據個數
int HPSize(HP* php);//打印堆
void HPPrint(HP* php);

Heap.c

//
//  Heap.c
#include "Heap.h"void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
void HPDestroy(HP* php)
{assert(php);free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}void swap(HPDataType* a, HPDataType* b)
{HPDataType tmp = *a;*a = *b;*b = tmp;
}
//logn
void AjustUp(HPDataType* arr, int child)
{//一直向上比較并調整 直到遇到根節點while(child > 0){//計算父節點int parent = (child - 1)/2;//比較//建小堆 <//建大堆 >if(arr[child] < arr[parent]){swap(&arr[child], &arr[parent]);child = parent;}else{break;}}
}
//logn
void AJustDown(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 HPPush(HP* php, HPDataType x)
{assert(php);//空間不夠 擴容if(php->capacity == php->size){int newCapacity = php->capacity==0 ? 4 : 2*php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->arr, sizeof(HPDataType)*newCapacity);//擴容失敗if(tmp == NULL){perror("Realoc Failed!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}php->arr[php->size++] = x;//向上調整AjustUp(php->arr, php->size-1);
}bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}void HPPop(HP* php)
{assert(!HPEmpty(php));swap(&php->arr[0], &php->arr[php->size-1]);--php->size;AJustDown(php->arr, 0, php->size);
}HPDataType HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}
int HPSize(HP* php)
{return php->size;
}void HPPrint(HP* php)
{for(int i = 0; i < php->size; i++)printf("%d ", php->arr[i]);printf("\n");
}

main.c

//
//  main.c
#include "Heap.h"
void test(void)
{//建小堆HP hp;HPInit(&hp);HPPush(&hp, 29);HPPush(&hp, 17);HPPush(&hp, 14);HPPush(&hp, 31);HPPush(&hp, 22);HPPush(&hp, 12);HPPrint(&hp);printf("size: %d, top: %d\n", HPSize(&hp), HPTop(&hp));int k = 5;while(k--){HPPop(&hp);HPPrint(&hp);printf("size: %d, top: %d\n", HPSize(&hp), HPTop(&hp));}HPDestroy(&hp);HPPrint(&hp);
}
int main(void)
{test();return 0;
}

3.4.運行結果

請添加圖片描述

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

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

相關文章

區間樹:多維數據的高效查詢

區間樹&#xff1a;多維數據的高效查詢 大家好&#xff0c;今天我們來探討一個在計算機科學中非常有趣且實用的數據結構——區間樹。想象一下&#xff0c;你是一位城市規劃師&#xff0c;需要快速找出某個區域內所有的醫院、學校或商場。或者你是一位游戲開發者&#xff0c;需要…

SQL 魔法:LEFT JOIN 與 MAX 的奇妙組合

一、引言 在數據庫操作的領域中&#xff0c;數據的關聯與聚合處理是核心任務之一。LEFT JOIN作為一種常用的連接方式&#xff0c;能夠將左表中的所有記錄與右表中滿足連接條件的記錄進行關聯&#xff0c;即便右表中沒有匹配的記錄&#xff0c;左表的記錄也會被保留&#xff0c;…

手寫tomcat

package com.qcby.annotation;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;Target(ElementType.TYPE)// 表示該注解只能用于類上 Retention(Retentio…

Android平臺下openssl動態庫編譯

1. 下載Linux平臺下的NDK軟件包 NDK 下載 | Android NDK | Android Developers 下載完成后執行解壓命令 # unzip android-ndk-r27d-linux.zip 2. 下載openssl-1.1.1w源碼包&#xff0c;并解壓 # tar -xzvf openssl-1.1.1w.tar.gz 3. 進入解壓后的openssl-1.1.1w目錄 …

【C++基礎】面試高頻考點解析:extern “C“ 的鏈接陷阱與真題實戰

名稱修飾&#xff08;Name Mangling&#xff09;是C為支持重載付出的代價&#xff0c;而extern "C"則是跨越語言邊界的橋梁——但橋上的陷阱比橋本身更值得警惕 一、extern "C" 的核心概念與高頻考點1.1 鏈接規范與名字改編機制C 為支持函數重載&#xff0…

OpenCV 官翻 4 - 相機標定與三維重建

文章目錄相機標定目標基礎原理代碼配置校準去畸變1、使用 cv.undistort()2、使用**重映射**方法重投影誤差練習姿態估計目標基礎渲染立方體極線幾何目標基礎概念代碼練習從立體圖像生成深度圖目標基礎概念代碼附加資源練習相機標定 https://docs.opencv.org/4.x/dc/dbb/tutori…

Python類中方法種類與修飾符詳解:從基礎到實戰

文章目錄Python類中方法種類與修飾符詳解&#xff1a;從基礎到實戰一、方法類型總覽二、各類方法詳解1. 實例方法 (Instance Method)2. 類方法 (Class Method)3. 靜態方法 (Static Method)4. 抽象方法 (Abstract Method)5. 魔術方法 (Magic Method)三、方法修飾符對比表四、綜合…

VSCode使用Jupyter完整指南配置機器學習環境

接下來開始機器學習部分 第一步配置環境&#xff1a; VSCode使用Jupyter完整指南 1. 安裝必要的擴展 打開VSCode&#xff0c;按 CtrlShiftX 打開擴展市場&#xff0c;搜索并安裝以下擴展&#xff1a; 必裝擴展&#xff1a; Python (Microsoft官方) - Python語言支持Jupyter (Mi…

數據結構與算法之美:拓撲排序

Hello大家好&#xff01;很高興我們又見面啦&#xff01;給生活添點passion&#xff0c;開始今天的編程之路&#xff01; 我的博客&#xff1a;<但凡. 我的專欄&#xff1a;《編程之路》、《數據結構與算法之美》、《C修煉之路》、《Linux修煉&#xff1a;終端之內 洞悉真理…

Ubuntu18.04 系統重裝記錄

Ubuntu18.04 系統重裝記錄 1 安裝google拼音 https://blog.csdn.net/weixin_44647619/article/details/144720947 你好&#xff01; 這是你第一次使用 Markdown編輯器 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章&#xff0c;了解一下Markdo…

Maven常用知識總結

Maven常用知識總結Maven 安裝與配置windows mvn安裝與配置IntelliJ IDEA 配置IntelliJ IDEA 配置系統mavenIntellij IDEA Maven使用IntelliJ IDEA 不能運行項目常見問題pom.xml 常用標簽講解parentgroupId artifactId versiondependencypropertiespluginpackagingdependencyMan…

PHP框架在大規模分布式系統的適用性如何?

曾幾何時&#xff0c;PHP被貼上“只適合小網站”的標簽。但在技術飛速發展的今天&#xff0c;PHP框架&#xff08;如Laravel、Symfony、Hyperf、Swoft等&#xff09; 早已脫胎換骨&#xff0c;勇敢地闖入了大規模分布式系統的疆域。今天&#xff0c;我們就來聊聊它的真實戰斗力…

DC-DC降壓轉換5.5V/3A高效率低靜態同步降壓轉換具有自適應關斷功能

概述&#xff1a;PC1032是一款高效且體積小巧的同步降壓轉換器&#xff0c;適用于低輸入電壓應用。它是緊湊設計的理想解決方案。其2.5V至5.5V的輸入電壓范圍適用于幾乎所有電池供電的應用。在中等至重負載范圍內&#xff0c;它以1.5MHz&#xff08;典型值&#xff09;的PWM模式…

min_25篩學習筆記+牛客多校02E

本來沒有學習這種較難的算法的想法的&#xff0c;因為比賽也做不到這種難度的題&#xff0c; 但是最近打牛客多校02&#xff0c;有一題要求 [1,n][1,n][1,n] 中素數的個數&#xff0c;我以為是像莫反一樣容斥&#xff0c;但是后面感覺不行。賽后知道是用 min_25 篩來求&#xf…

FunASR Paraformer-zh:高效中文端到端語音識別方案全解

項目簡介 FunASR 是阿里巴巴達摩院開源的端到端語音識別工具箱,集成了多種語音識別、語音活動檢測(VAD)、說話人識別等模塊。其中 paraformer-zh 和 paraformer-zh-streaming 是針對中文語音識別任務優化的端到端模型,分別適用于離線和流式場景。Paraformer 采用并行 Tran…

數據結構自學Day9: 二叉樹的遍歷

一、二叉樹的遍歷“遍歷”就是按某種規則 依次訪問樹中的每個節點&#xff0c;確保 每個節點都被訪問一次且只訪問一次遍歷&#xff1a;前序 中序 后序&#xff08;深度優先&#xff09;&#xff0c;層序&#xff08;廣度優先&#xff09;類型遍歷方法特點深度優先遍歷前序、中…

Leetcode(7.16)

求二叉樹最小深度class Solution {public int minDepth(TreeNode root) {if (root null) {return 0;}Queue<TreeNode> queue new LinkedList<>();queue.offer(root);int depth 0;while (!queue.isEmpty()) {depth;int levelSize queue.size();for (int i 0; i…

Go從入門到精通(25) - 一個簡單web項目-實現鏈路跟蹤

Go從入門到精通(25) 一個簡單web項目-實現鏈路跟蹤 文章目錄Go從入門到精通(25)前言為什么需要分布式鏈路跟蹤&#xff1f;go實現鏈路跟蹤搭建zipkin 服務安裝依賴添加tracing包&#xff0c;OpenTelemetry 和Zipkin在 Gin 中集成 OpenTelemetry 中間件log包添加獲取traceId方法…

2025年最新秋招java后端面試八股文+場景題

一、Java核心八股文&#xff08;2025年最新版&#xff09;1. Java基礎HashMap vs ConcurrentHashMapHashMap&#xff1a;非線程安全&#xff0c;JDK1.8后采用數組鏈表/紅黑樹&#xff0c;擴容時可能死循環&#xff08;JDK1.7&#xff09;。ConcurrentHashMap&#xff1a;JDK1.8…

esp32 sd卡

ref&#xff1a; platform io & arduino Boards — PlatformIO latest documentation https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/README.md SD 卡實驗 | 極客俠GeeksMan GitHub - fabianoriccardi/ESPLogger: An Arduino library pro…