当前位置: 首页 > news >正文

数据结构-二叉树_堆

一.   树的概念

   树在我们的日常生活中随处可见,人们将生活中的树转换成存放数据的树形结构,就成了数据结构中的“树”。

如上图所示,自然界中的树有树根,有树枝,有树叶,当我们将其转换成树形结构时, 只需将其倒转过来,将树根,树枝的岔口,树枝的顶部画一个圆,就成了一个完美的树形结构:

       树型结构是一种非线性的数据结构,它是由N(N>0)个结点组成的一个数据的集合,我们称这种数据结构为“树”。

      树的根部结点称为根结点,根结点是没有前驱结点的,

      除了根结点外,其余结点被分成互不相交的集合,

      每个集合都是结构与树类型相似的子树,每棵子树的根结点有且仅有一个前驱结点,后          继结点可以有0个或多个。

      每棵子树是互不相交的,如果相交则为“图”不为“树”

      除了根结点外,每个结点都只有一个父结点

      假设一棵树有N个结点,那么这条数有N-1条边 

二.   树的相关术语

父结点:如果一个结点有子节点,那么这个结点是子节点的父节点

子结点: 一个结点含有的子树的根结点称为该结点的子结点

结点的度:一个结点有N个子结点,那么这个结点的度为N

树的度:最大的结点的度称为树的度

叶子结点:度为0的结点

分支结点:度不为0的结点

结点的层次:从根开始定义,根为第一层,根的子结点为第二层,以此类推

树的高度:树中结点的最大层次

三.   二叉树的概念

        二叉树是树的一种,也是我们最常用的树形结构,一棵二叉树是结点的一个有限集合,该集合由一个根结点和两棵分别称为“左子树”和“右子树”的二叉树组成。

上图是一棵二叉树,通过上图我们可知二叉树有以下特点:

1.   二叉树的结点的度不大于2

2.   二叉树的子树有左右之分,因此二叉树是有序树

3.   二叉树可以是空树,可以只有根结点,左子树可以为空,右子树可以为空,左右子树均在 

 

四.   满二叉树 

    对于每个结点的度数都达到最大值,我们称这种二叉树为满二叉树。

    一棵树的层数为N,最大结点数为2*N-1,下图是一棵满二叉树,层数为3,最大节点数为7

 五.   完全二叉树

      完全二叉树是由满二叉树引出来的,对于深度为K的,有N个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1到N的结点一一对应时称为完全二叉树。

注意:满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树

六.   顺序结构的二叉树

顺序结构的二叉树底层为数组,但是只适合完全二叉树,不然会造成空间的浪费

对于完全二叉树,因为其结点都有连续对应的编号,因此适合用数组存储,数组的下标代替了结点的编号

对于非完全二叉树,如果使用数组来实现,有内存空间的浪费:

七.   顺序结构二叉树的实现(本文以小堆为例)

我们一般用堆来实现顺序结构的数组,堆其实是一种特殊的二叉树,它有着二叉树的性质,也有许多其他的特性,同时堆也是一棵完全二叉树

堆也分为大堆和小堆:

大堆:根结点存放的数据最大,每个父结点都比其子结点要大

小堆:根结点存放的数据最小,每个父结点都比其子结点要小 

 

子结点与父结点的关系: 

对于子结点:   若子结点 i 在该堆中有效,则其父结点对应的序号为:(i-1)/2,当i=0时,无

父结点,因为i=0时为根结点,根结点没有父结点

对于父结点:若父结点的序号为i,  左孩子序号为2*i+1,右孩子序号为:2*i+2,

对于结点数位n的完全二叉树,若2*i+1>=n,则无左孩子,       若2*i+2>=n,则无右孩子

(都大于最大结点数了,不可能还有子结点的,总不能凭空加上去吧)

1.堆的结构定义 

堆的底层结构是数组,和顺序表很相似,其实在定义结构体中也是十分相似的

typedef int HPDataType;
//堆的定义
//堆的底层为数组
typedef struct Heap {HPDataType* arr;//指向动态开辟内存的地址int size;//堆的有效数据个数int capacity;//堆的最大容量
}HP;

 2.堆的初始化

初始化操作与顺序表一致,这里就不再赘述

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

3.堆的销毁

销毁操作与顺序表一致,不再赘述

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

4.入堆

       在入堆操作时,是在堆尾进行插入的,在插入前需要检查当前堆的空间是否足够容纳新数据,空间不足的话需要先扩容再进行入堆。

       因为堆分为大堆和小堆,我们在入堆操作后,新数据所在的位置不一定满足堆的性质,这里我们所以我们需要将新数据向上调整,直到新数据所处的位置满足堆的性质,新数据调整完后更新堆的有效个数。

void HPPush(HP* php, HPDataType x) {assert(php);//检查空间是否不足if (php->capacity == php->size) {//如果堆空间为0,扩容int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* ret = (HPDataType*)realloc(php->arr, newcapacity*sizeof(HPDataType));if (ret == NULL) {perror("relloc");exit(1);}php->arr = ret;php->capacity = newcapacity;}//在当前位置入堆php->arr[php->size] = x;AdjustUp(php->arr,php->size);//堆的有效个数+1,为什么要到最后才更新堆的有效个数,//因为向上调整法需要传递新插入结点的下标,插入堆后下标刚好是size//当然更新有效个数后再进行向上调整也是可以的,修改向上调整函数的第二个参数就行++ php->size;
}

那么,对入堆数据的向上调整时是如何实现的呢?

5.向上调整法(堆的插入后需要调整)

毫无疑问,入堆的数据成为了某一个结点的子结点,向上调整我们需要知道当前结点的父结点,

利用当前结点在数组所对应的下标,入堆对应的下标其实就是有效数据个数size

新结点入堆后,开始进行向上调整,当前结点与其父结点相比较, 然后更新父结点与子结点,因为我们建立的是小堆,所以当子结点大于父结点时,便不再需要继续向上调整

//交换函数
void Swap(HPDataType* x, HPDataType* y) {HPDataType tmp = *x;*x = *y;*y = tmp;
}//向上调整算法,子求父: (子-1)/2
void AdjustUp(HPDataType* arr, int child) {int parent = (child - 1) / 2;while (child > 0) {//确保当前节点 child 有一个有效的父节点可以进行比较和调整。if (arr[child] < arr[parent]) {Swap(&arr[child], &arr[parent]);//传址调用}else {break;}}
}

向上调整法图示: 

6.出堆

对于堆而言:出堆的结点是堆顶,也就是树的根结点

       你或许会想:堆的底层结构为数组,那么我们直接将数组下标为0的数据删除就好。那么真的可以这样操作吗?

如下图,假如我们直接将数组下标为0的结点直接出堆,出堆后既不是大堆也不是小堆,而且原本结点的父子关系也错误了

正确的出堆方法:将堆顶的结点与堆底相调换,再删除堆底结点,再让新的堆顶结点向下调整

因为数组最后一位删除后,堆的结构不会发生改变

 

 

堆顶和堆底相交换后,删除新的堆底,然后新的堆顶向下调整,如果子结点比父结点大,则两者向交换,直到子结点比父结点小或者子结点处于非法范围

 

7.向下调整法(堆的删除后需要调整) 

在小堆的出堆后,要进行向下调整的结点毫无疑问是堆顶,向下调整,我们需要知道其最小的子结点,当我们找到最小的子结点后,将最小的子结点与父结点比较,如果子结点比父结点小,则交换,然后更新父结点和子结点。否则不需要向下调整

//向下调整算法,父求子:父*2+ 1/2
void AdjustDown(HPDataType* arr, int parent, int n) {//n 是数组的大小int child = parent*2 + 1;//假设左孩子最小while (child < n)//孩子不能超过数组的界限{//找左右孩子中找最小的if (child + 1 < n && arr[child] > arr[child + 1])//child + 1 < n检查是否有右孩子,没有右孩子就不进入{child++;}if (arr[child] < arr[parent])//父比子大,交换,因为是形成小堆,最小的要在堆顶{Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

8.堆的判空

返回堆的有效数据个数即可判断堆是否为空

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

9.取堆顶数据 

//取堆顶
HPDataType HPTop(HP* php)
{assert(php && php->size);return php->arr[0];
}

八.全码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HPDataType;
//堆的定义
//堆的底层为数组
typedef struct Heap {HPDataType* arr;int size;int capacity;
}HP;
//小堆
//初始化
void HPInit(HP* php) {assert(php);php->arr = NULL;php->size = php->capacity = 0;
}//销毁
void HPDestroy(HP* php) {assert(php);if (php->arr) {free(php->arr);}php->capacity = php->size = 0;php->arr = NULL;
}//交换函数
void Swap(HPDataType* x, HPDataType* y) {HPDataType tmp = *x;*x = *y;*y = tmp;
}
//向上调整算法,子求父: (子-1)/2
void AdjustUp(HPDataType* arr, int child) {int parent = (child - 1) / 2;while (child > 0) {//确保当前节点 child 有一个有效的父节点可以进行比较和调整。if (arr[child] < arr[parent]) {Swap(&arr[child], &arr[parent]);}else {break;}}
}//向下调整算法,父求子:父*2+ 1/2
void AdjustDown(HPDataType* arr, int parent, int n) {//n 是数组的大小int child = parent*2 + 1;//假设左孩子最小while (child < n)//孩子不能超过数组的界限{//找左右孩子中找最小的if (child + 1 < n && arr[child] > arr[child + 1])//child + 1 < n检查是否有右孩子,没有右孩子就不进入{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) {//如果堆空间为0,扩容int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* ret = (HPDataType*)realloc(php->arr, newcapacity*sizeof(HPDataType));if (ret == NULL) {perror("relloc");exit(1);}php->arr = ret;php->capacity = newcapacity;}//在当前位置入堆php->arr[php->size] = x;AdjustUp(php->arr,php->size);//堆的有效个数+1,为什么要到最后才更新堆的有效个数,//因为向上调整法需要传递新插入结点的下标,插入堆后下标刚好是size++ php->size;}//出堆
void HPPop(HP* php) {//出堆,出的是堆顶,但是不能直接删除堆顶,这样堆就混乱了//将堆顶元素和顶底交换,然后删除堆底//最后向上调整算法向上调整assert(php);Swap(&php->arr[0], &php->arr[php->size - 1]);-- php->size;AdjustDown(php->arr, 0, php->size);}//判空
bool HPEmpty(HP* php) {assert(php);return php->size==0;
}//取堆顶
HPDataType HPTop(HP* php)
{assert(php && php->size);return php->arr[0];
}

相关文章:

数据结构-二叉树_堆

一. 树的概念 树在我们的日常生活中随处可见&#xff0c;人们将生活中的树转换成存放数据的树形结构&#xff0c;就成了数据结构中的“树”。 如上图所示&#xff0c;自然界中的树有树根&#xff0c;有树枝&#xff0c;有树叶&#xff0c;当我们将其转换成树形结构时&#xf…...

Vscode+Pycharm+Vue.js+WEUI+django火锅(三)理解Vue

新创建的Vue项目里面很多文件&#xff0c;对于新手&#xff0c;老老实实做一下了解。 1.框架逻辑 框架的逻辑都是相通的&#xff0c;花点时间理一下就清晰了。 2.文件目录及文件 创建好的vue项目下&#xff0c;主要的文件和文件夹要先认识一下&#xff0c;并与框架逻辑对应起…...

溯变:守护天使 | OPENAIGC开发者大赛企业组优秀作品

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…...

android中byte[] buf没有结束符,new String(buf)会不会出错?

答案是&#xff1a;不会 看例子&#xff1a; 这和c是不一样的&#xff0c;不需要特别的在字符串后面添加一个\0结束....

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…...

介绍一款开源的 Modern GUI PySide6 / PyQt6的使用

首先附上大神的开源地址&#xff08;自行克隆吧&#xff09;&#xff1a; https://github.com/Wanderson-Magalhaes/Modern_GUI_PyDracula_PySide6_or_PyQt6 步骤一&#xff1a;安装PySide6库 pip install PySide6 步骤二&#xff1a;运行main文件 python main.py 就得…...

【大模型】AI数据基础设施的对象存储

官网地址&#xff1a; MinIO | S3 Compatible Storage for AI Github地址&#xff1a; ​​​​​https://github.com/minio/minio 企业级&#xff0c;并对AI准备就绪的分布式对象存储&#xff08;一般拿来存模型文件&#xff09; 部署步骤参考&#xff1a; minio安装部署及…...

【前端工程解耦】使用事件中心实现系统解耦,注册,触发,删除事件

前言 事件中心提供了一种灵活且可扩展的方式来管理事件和处理函数之间的关系&#xff0c;同时保持它们之间的解耦&#xff0c;可以降低系统耦合度&#xff0c;将视图和逻辑拆分出来&#xff0c;还是那句话&#xff0c;如果一个中间件解决不了问题&#xff0c;那就再加一个 废话…...

计算机网络803-(4)网络层

目录 1.虚电路服务 虚电路是逻辑连接 2.数据报服务 3.虚电路服务与数据报服务的对比 二.虚拟互连网络-IP网 1.网络通信问题 2.中间设备 3.网络互连使用路由器 三.分类的 IP 地址 1. IP 地址及其表示方法 2.IP 地址的编址方法 3.分类 IP 地址 &#xff08;1&#x…...

java速成指南

密码都是 123 适用于php .net 7天转java 【腾讯文档】快速上手培训-阿龙 分享给你多个文件 https://docs.qq.com/s/jUcRQ4VPA4grzx8SPYzrBa 第一节 安装jdk,maven,idea_哔哩哔哩_bilibili...

【Unity】双摄像机叠加渲染

一、前言 之前我在做我的一个Unity项目的时候&#xff0c;需要绘制场景网格的功能&#xff0c;于是就用到了UnityEngine.GL这个图形库来绘制&#xff0c;然后我发现绘制的网格线是渲染在UI之后的&#xff0c;也就是说绘制出来的图形会遮盖在UI上面&#xff0c;也就导致一旦这些…...

web网页项目--用户登录,注册页面代码

index.html <!DOCTYPE html> <html lang"zxx"><head><title>xxx注册</title><!-- Meta tags --><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…...

国外火出圈儿的PM御用AI编程工具Bolt.new效果干不过国产的CodeFlying?号称全新定义全栈开发流程?

不知道大家最近有没有发现国外的很多AI都在挤破脑袋想去提升大模型的编程能力&#xff0c; 离我们最近的是上周Openai 发布的全新模型GPT-4o-Canvas&#xff0c; 拥有超强的代码编写能力。 另外还有LlamaCoder、Cursor、Claude artifacts、Replit... 光是今年一年就推出了好…...

爸妈总说着学门技术,学机器视觉技术确实是一条踏实的生活道路,这条路你走得下去走得通吗?

你爸妈说的对&#xff0c;有一技之长终身受益&#xff0c;人要有一技傍身。学一门技术是稳定职业与生活的基本的保障&#xff0c;但是与其盲目的选择一门技术&#xff0c;都是成年人&#xff0c;不如思考下这门技术给自我带来经济效益&#xff0c;在这一方面可以详细咨询我。 …...

2024互联网下载神器IDM6.42你值得拥有

&#x1f525; 互联网下载神器大揭秘&#xff01;IDM6.42你值得拥有 &#x1f680; Hey&#xff0c;各位小伙伴们&#xff0c;今天我要给你们安利一款我超爱的软件——Internet Download Manager 6.42&#xff08;简称IDM&#xff09;&#xff0c;这款下载器简直就是下载界的“…...

基于H3C环境的实验——OSPF

目录 实验设备和环境 实验设备 实验环境 实验记录 1、单区域 OSPF基本配置 步骤1:搭建实验环境并完成基本配置 步骤2:检查网络连通性和路由器路由表。 步骤3:配置OSPF 步骤4:检查路由器OSPF邻居状态及路由表 实验设备和环境 实验设备 三台路由器、两台PC、电源线、两…...

java线程池详解

在Java中&#xff0c;线程池是一种重要的多线程处理方式&#xff0c;通过管理和复用线程&#xff0c;提高应用程序的性能和响应速度&#xff0c;减少线程创建和销毁的开销&#xff0c;避免线程数量过多导致系统负载过高的问题。本文将详细介绍Java线程池的概念、核心参数、工作…...

五、创建型(建造者模式)

建造者模式 概念 建造者模式是一种创建型设计模式&#xff0c;通过使用多个简单的对象一步步构建一个复杂的对象。它将一个复杂对象的构建过程与其表示分离&#xff0c;从而使同样的构建过程可以创建不同的表示。 应用场景 复杂对象构建&#xff1a;当一个对象有多个属性&…...

CPU超线程技术是什么,怎么启用超线程技术

超线程技术是一种允许单个物理CPU核心模拟成两个逻辑核心的技术&#xff0c;从而提升处理器的并行性能和效率。以下是对超线程技术的详细介绍&#xff1a; 基本概念&#xff1a;超线程&#xff08;Hyper-Threading&#xff0c;HT&#xff09;是Intel公司研发的一种技术&#x…...

vba学习系列(7)--考勤表制作

系列文章目录 文章目录 系列文章目录前言一、汇总所有工作表指定区域内容到指定工作表二、汇总所有工作表指定区域内容到指定工作表(带公式)总结 前言 一、汇总所有工作表指定区域内容到指定工作表 Sub CopyRangesToSummary()Dim sourceSheet As WorksheetDim targetSheet As…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

适应性Java用于现代 API:REST、GraphQL 和事件驱动

在快速发展的软件开发领域&#xff0c;REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名&#xff0c;不断适应这些现代范式的需求。随着不断发展的生态系统&#xff0c;Java 在现代 API 方…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...

CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?

在现代前端开发中&#xff0c;Utility-First (功能优先) CSS 框架已经成为主流。其中&#xff0c;Tailwind CSS 无疑是市场的领导者和标杆。然而&#xff0c;一个名为 UnoCSS 的新星正以其惊人的性能和极致的灵活性迅速崛起。 这篇文章将深入探讨这两款工具的核心理念、技术差…...