【数据结构】揭秘二叉树与堆--用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-12k1
请添加图片描述

2.2.2.完全二叉树

完全二叉树是由满二叉树得来的,最后一层的结点个数不一定达到最大,其他层结点个数都到达最大值,且结点从左到右排列

请添加图片描述

2.3.二叉树的特性

规定根结点的层数为1:

  1. 二叉树第i层上最多有2i−12^{i-1}2i1个结点
  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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…