数据结构:二叉树的数组结构以及堆的实现详解
目录
一.树与二叉树
1.树的概念与相关术语:
2.二叉树:
(1)定义:
(2)特殊的二叉树:
(3)完全二叉树
(4)二叉树的存储结构:
二.堆的概念与性质
1.堆的概念:
2.堆的重要性质:
三.堆的具体代码实现
1.堆的结构:
2.堆的初始化与销毁:
3.向上调整算法和向下调整算法:
(1)向上调整算法:
(2)向下调整算法:
(3)总结两种算法的区别:
(4)堆元素的插入:
(5)堆元素的删除:
(6)取堆顶元素:
(7)堆的判空:
四.堆的具体应用
1.基于已有堆结构的堆排序(通过不断取堆顶):
2.数组建堆,先向下调整建堆,再向上调整:
一.树与二叉树
1.树的概念与相关术语:
概念
树是⼀种非线性的数据结构,它是由 n(n>=0) 个有限结点组成⼀个具有层次关系的集合。把它叫做 树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,而叶朝下的。
有一个特殊的结点,称为根结点,根结点没有前驱结点。
除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、……、Tm ,其中每⼀个集合 Ti( 又是⼀棵结构与树类似的子树。每棵⼦树的根结点有且只有⼀个前驱,可以 有 0 个或多个后继。因此,树是递归定义的
相关术语
父结点/双亲结点:若⼀个结点含有子结点,则这个结点称为其子结点的父结点
子结点/孩子结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点
结点的度:⼀个结点有⼏个孩⼦,他的度就是多少
树的度:⼀棵树中,最⼤的结点的度称为树的度
叶子结点/终端结点:度为 0 的结点称为叶结点
分支结点/非终端结点:度不为 0 的结点
兄弟结点:具有相同父结点的结点互称为兄弟结点(亲兄弟)
结点的层次:从根开始定义起,根为第 1 层,根的⼦结点为第 2 层,以此类推
树的高度或深度:树中结点的最大层次
路径:⼀条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列
2.二叉树:
(1)定义:
二叉树满足以下两个特点:
1. 二叉树不存在度大于 2 的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此⼆叉树是有序树
(2)特殊的二叉树:
满二叉树
⼀个二叉树,如果每⼀个层的结点数都达到最⼤值,则这个二叉树就是满二叉树。也就是说,如果⼀ 个二叉树的层数为 K ,且结点总数是2的k次方-1 ,则它就是满二叉树。
(3)完全二叉树
完全二叉树是效率很高的数据结构,完全二叉树是由满⼆叉树而引出来的。
对于深度为 K 的,有 n 个 结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从 1 至 n 的结点⼀⼀对应时称 之为完全⼆叉树。要注意的是满⼆叉树是⼀种特殊的完全⼆叉树
简单来说:完全二叉树和满二叉树的区别·就在于那它们的区别主要在于结构上的差异,满二叉树要求每一层都填满,而完全二叉树只要求前几层填满,最后一层可以填到最左边但不一定全部填满
(4)二叉树的存储结构:
顺序存储(即下面我要说的堆)与链式结构
(我在这篇文章主要说明的是二叉树的顺序存储,链式结构的介绍我会详细再另外写一篇文章)
二.堆的概念与性质
1.堆的概念:
堆(Heap)是计算机科学中的一种特殊数据结构,它可以被视为一棵完全二叉树的数组表示形式。堆的特点是节点的值总是不大于或不小于其父节点的值,这种性质使得堆的根节点总是该数据结构中的最大值或最小值
其中,堆的根节点是该数据结构中的最大值则称该堆为大根堆;
堆的根节点是该数据结构中的最小值则称该堆为小根堆。
堆满足以下特性:
*堆中某个结点的值总是不大于或不小于其父结点的值
*堆总是⼀棵完全二叉树
2.堆的重要性质:
对于具有 n 个结点的完全⼆叉树,如果按照从上至下从左至右的数组顺序对所有结点从 0 开始编号,则对于序号为 i 的结点有:
1. 若 i>0 , i 位置结点的双亲序号: (i-1)/2 ; i=0 , i 为根结点编号,无双亲结点
2. 若 2i+1,左孩子序号: 2i+1 , 2i+1>=n 否则无左孩子
3. 若 2i+2,右孩子序号: 2i+2 , 2i+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;
}//堆的销毁
void HPDestroy(HP* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->capacity = php->size = 0;
}
3.向上调整算法和向下调整算法:
(1)向上调整算法:
目标:
将新插入的节点或被修改的子节点调整到合适位置,使其满足堆性质
步骤:
从当前节点开始,比较其与父节点的值
如果违反堆性质(如最小堆中子节点值更小),则交换两者
重复上述过程,直到到达根节点或满足堆性质
void AdjustUp(HP* hp, int child)//传入堆和调整的起始孩子
{int parent = (child - 1) / 2;//公式while (child > 0)//注意退出条件{//建小根堆if (hp->arr[parent] > hp->arr[child]){swap(&hp->arr[parent], &hp->arr[child]);child = parent;parent = (child - 1) / 2;}else{break;//如果不用换了。说明这个堆正常了,和adjustdown的退出原因一样}}
}
(2)向下调整算法:
目标:
将删除根节点后的最后一个元素调整到堆顶,或修复被修改的根节点
步骤:
找到当前节点的子节点(左、右),选出符合堆性质的子节点(如最小堆中较小的子节点)
如果当前节点违反堆性质(如比子节点更大),则与子节点交换
重复上述过程,直到叶子节点或满足堆性质
void AdjustDown(HP* hp, int parent, int n)//n是向下调整的下界范围
{int child = parent * 2 + 1;//公式while (child < n){//建小根堆if (child + 1 < n && hp->arr[child + 1] < hp->arr[child])//child+1的目的是确保两个孩子结点都是有意义的结点{child++;}if (hp->arr[parent] > hp->arr[child]){swap(&hp->arr[parent], &hp->arr[child]);parent = child;child = parent * 2 + 1;}else{//我最小的孩子都比父节点大,那么这个堆就是正常堆了,直接退出break;}}
}
(3)总结两种算法的区别:
1. 方向不同
向上调整:从子节点向根节点方向调整
例如,插入新节点时,将其放在堆末尾,然后逐层与父节点比较并交换
向下调整:从父节点向叶子节点方向调整
例如,删除根节点后,将末尾节点移到根位置,逐层与子节点比较并交换
2. 应用场景不同
向上调整:
插入新元素:新元素被添加到堆的末尾,可能破坏堆性质,需向上调整
节点值增大:在最大堆中,若某节点的值变大,可能需要向上调整
向下调整
删除根节点:删除堆顶后,将末尾节点移到堆顶,需向下调整
节点值减小:在最大堆中,若某节点的值变小,可能需要向下调整
构建堆:从最后一个非叶子节点开始,逐个向下调整,时间复杂度为 O(n)
3. 操作步骤不同
向上调整:
比较当前节点与父节点
若违反堆性质(如最大堆中当前节点值更大),则交换两者
重复直到到达根节点或满足堆性质
向下调整:
比较当前节点与左右子节点
选择最大(或最小)的子节点(根据堆类型)
若子节点违反堆性质,则交换两者
重复直到到达叶子节点或满足堆性质
4. 时间复杂度
常数因子差异:向下调整需比较两个子节点,操作略复杂;向上调整仅需比较父节点以下是时间复杂度的具体算法
(4)堆元素的插入:
void HeapPush(HP* hp, int x)
{assert(hp);//判断空间是否已满if (hp->capacity == hp->size){//注意capacity的单位是个,不是字节int newcapacity = hp->capacity == 0 ? 4 : 2 * hp->capacity;//第一次开就开四个//空间开辟//注意这里是realoc不是malloc//所有链式结构的用malloc,咱们开辟新节点//所有线性结构,底层是数组的,咱们就realoc//因为咱们要保留数组之前的数据,所以必须要扩容而不是重新开辟HP* newspace = (HP*)realloc(hp->arr, newcapacity * sizeof(int));if (newspace == NULL){perror("malloc wrong!");exit(1);}hp->arr = newspace;hp->capacity = newcapacity;}//直接插入//size是当前有效元素个数,对应的也是下一个该存放元素位置的下标hp->arr[hp->size] = x;hp->size++;//插入元素之后配套进行向上调整AdjustUp(hp, hp->size - 1);//注意传入的是下标
}
(5)堆元素的删除:
void HeapPop(HP* hp)
{//几乎所有的数据结构,在插入元素是要断言这个数据结构是否存在//在删除元素的时候断言这个数据结构中要含有元素assert(!empty(hp));//删除根元素swap(&hp->arr[0], &hp->arr[hp->size - 1]);hp->size--;//配套使用向下调整算法AdjustDown(hp, 0, hp->size);
}
(6)取堆顶元素:
int HeapTop(HP* hp)
{assert(!empty(hp));//empty里面判断了这个堆必须存在,所以说不用再在这判断了、return hp->arr[0];
}
(7)堆的判空:
bool empty(HP* hp)
{assert(hp);return hp->size == 0;
}
四.堆的具体应用
1.基于已有堆结构的堆排序(通过不断取堆顶):
// 1、需要堆的数据结构
// 2、空间复杂度 O(N)
void HeapSort(int* a, int n)
{HP hp;for(int i = 0; i < n; i++){HPPush(&hp,a[i]);}int i = 0;while (!HPEmpty(&hp)){a[i++] = HPTop(&hp);HPPop(&hp);}HPDestroy(&hp);
}
2.数组建堆,先向下调整建堆,再向上调整:
// 升序,建⼤堆
// 降序,建⼩堆
// O(N*logN)
void HeapSort(int* a, int n)
{// a数组直接建堆 O(N)for (int i = (n-1-1)/2; i >= 0; --i){AdjustDown(a, n, i);}// O(N*logN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}
欧克,到这里就差不多把数组结构的堆介绍完了,更多的就是堆的Top-K问题了,感兴趣的可以先去了解,我后续会写关于它的解析
那就这样吧
全文终
相关文章:

数据结构:二叉树的数组结构以及堆的实现详解
目录 一.树与二叉树 1.树的概念与相关术语: 2.二叉树: (1)定义: (2)特殊的二叉树: (3)完全二叉树 (4)二叉树的存储结构&#x…...

AWS S3 如何设置公开访问权限?
1.让整个bucket都有公开访问权限 1.1关闭【阻止公共读】 1.2关闭ACL访问控制 1.3打开桶策略 这样桶内所有的图片就能访问了 2.只开放特定文件让其具有访问权限? 2.1关闭【阻止公共读】 如之前的图示 2.2打开ACL控制 2.3单个文件打开公共读...

使用TortoiseGit配合BeyondCompare实现在Git仓库中比对二进制文件
使用TortoiseGit的比对工具可以直接右键,点击选择比对和上一版本的变化差异: 但是TortoiseGit只能支持比对纯文本文件的变化差异,如果尝试比对二进制文件,会提示这不是一个有效的文本文件: BeyondCompare可以比对二进制…...

8、HTTP/1.0和HTTP/1.1的区别【高频】
第一个是 长连接: HTTP/1.0 默认 短连接,(它也可以指定 Connection 首部字段的值为 Keep-Alive实现 长连接)而HTTP/1.1 默认支持 长连接,HTTP/1.1是基于 TCP/IP协议的,创建一个TCP连接是需要经过三次握手的…...

Rk3568驱动开发_开发环境的搭建_1
1、环境说明: 需要用官方的程序包,这个程序需要在虚拟机里编译再将镜像烧录到板子里,本质上是给板子上一套Linux操作系统,镜像是.img文件 Linux操作系统被分成了多个模块,编译好后储存在镜像里,本质上就和…...

Solr中得Core和Collection的作用和关系
Solr中得Core和Collection的作用和关系 一, 总结 在Apache Solr中,Core和Collection 是两个核心概念,他们分别用于单机模式和分布式模式(SolrCloud)中,用于管理和组织数据。 二,Core 定义&am…...

Visual Studio Code 远程开发方法
方法1 共享屏幕远程控制,如 to desk, 向日葵 ,像素太差,放弃 方法2 内网穿透 ssh 第二个方法又很麻烦,尤其是对于 windows 电脑,要使用 ssh 还需要额外安装杂七杂八的东西;并且内网穿透服务提供商提供的…...
如何看到 git 上打 tag 的时间
在 Git 中可以通过以下方法查看标签(tag)的创建时间: 使用 git show 命令: 运行以下命令可以查看某个特定标签的详细信息,包括创建时间: git show 输出中会包含 Tagger 的信息和 Date 字段,显示…...

【HarmonyOS Next】鸿蒙TaskPool和Worker详解 (一)
【HarmonyOS Next】鸿蒙TaskPool和Worker详解 (一) 一、TaskPool和Worker如何实现多线程?各自特点是什么? 在鸿蒙中通过TaskPool和Worker实现多线程并发,两者都基于Actor并发模型实现。 Actor并发模型,每…...
如何设置HTTPOnly和Secure Cookie标志?
设置HttpOnly和Secure标志于Cookie中是增强Web应用安全性的重要措施。这两个标志帮助防止跨站脚本攻击(XSS)和中间人攻击(MitM)。下面是关于如何设置这些标志的具体步骤: 设置方法 在服务器端设置 根据你的服务器端…...

几个api
几个api 原型链 可以阅读此文 Function instanceof Object // true Object instanceof Function // true Object.prototype.isPrototypeOf(Function) // true Function.prototype.isPrototypeOf(Object) // true Object.__proto__ Function.prototype // true Function.pro…...

Deepseek本地部署指南:在linux服务器部署,在mac远程web-ui访问
1. 在Linux服务器上部署DeepSeek模型 要在 Linux 上通过 Ollama 安装和使用模型,您可以按照以下步骤进行操作: 步骤 1:安装 Ollama 安装 Ollama: 使用以下命令安装 Ollama: curl -sSfL https://ollama.com/install.s…...

基于 DeepSeek+AutoGen 的智能体协作系统
用 AutoGen 实现智能体协作流程,假设团队里的 3 个角色,让 3 个角色相互交流后并给出不同方案,最后进行总结。下面是实现的思路,欢迎一起学习交流。 一、系统设计 1. sre_engineer_01 - 问题诊断与初步解决方案 职责:…...

博客系统笔记总结 2( Linux 相关)
Linux 基本使用和程序部署 基本命令 文件操作 显示当前目录下的文件 ls:显示当前目录下的文件 ll:以列表的形式展示,包括隐藏文件 进入目录 && 显示当前路径 cd:进入目录(后面跟相对路径或者绝对路径&…...

计算机毕业设计SpringBoot+Vue.js电影评论网站系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

精美登录注册UI,登录页面设计模板
精美登录注册UI,登录页面设计模板 引言 在网页设计中,按钮是用户交互的重要元素之一。一个炫酷的按钮特效不仅能提升用户体验,还能为网页增添独特的视觉吸引力。今天,我们将通过CSS和JavaScript来实现一个“精美登录注册UI,登录页面设计模板”。该素材呈现了数据符号排版…...
《Linux系统编程篇》共享内存(Linux 进程间通信(IPC))——基础篇
文章目录 引言什么是共享内存System V 共享内存 API 引入1. shmget2. shmat3. shmdt4. shmctl5. 结构体 shmid_ds 开始实操注意 结束 今天的你有没有前进一小步呢 ——家驹(StrangeHead) 引言 那么共享内存,我们如何去使用他呢,先来听笔者啰嗦一段话吧…...

【EB-03】 AUTOSAR builder与EB RTE集成
AUTOSAR builder与EB RTE集成 1. Import Arxml files to Tresos2. Run MultiTask Script3. Add Components3.1 Run EcuExtractCreator Script4. Mapping Component to Partitions5. Event Mapping/Runnables Mapping to Tasks6. Port Connect7. Run SvcAs_Trigger Script8. Ver…...

HTML——前端基础1
目录 前端概述 前端能做的事情编辑 两步完成一个网页程序 前端工具的选择与安装 HTML HTML5介绍 HTML5的DOCTYPE声明 HTML基本骨架 文字标签 标题之标签 标签之段落、换行、水平线 标签之图片 标签之超文本链接 标签之文本 列表标签之有序列表 列表标签之无序…...
AI回答:Linux C/C++编程学习路线
Linux C/C编程学习路线需要结合Linux系统特性和C/C语言的特点,以下是一个系统化的学习路径,适合从初学者到进阶者: 第一阶段:Linux基础 Linux操作系统基础 学习Linux基本命令:ls、cd、mkdir、rm、grep、find等。 理解…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...