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

数据结构:二叉树的数组结构以及堆的实现详解

目录

一.树与二叉树

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.树的概念与相关术语&#xff1a; 2.二叉树&#xff1a; &#xff08;1&#xff09;定义&#xff1a; &#xff08;2&#xff09;特殊的二叉树&#xff1a; &#xff08;3&#xff09;完全二叉树 &#xff08;4&#xff09;二叉树的存储结构&#x…...

AWS S3 如何设置公开访问权限?

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

使用TortoiseGit配合BeyondCompare实现在Git仓库中比对二进制文件

使用TortoiseGit的比对工具可以直接右键&#xff0c;点击选择比对和上一版本的变化差异&#xff1a; 但是TortoiseGit只能支持比对纯文本文件的变化差异&#xff0c;如果尝试比对二进制文件&#xff0c;会提示这不是一个有效的文本文件&#xff1a; BeyondCompare可以比对二进制…...

8、HTTP/1.0和HTTP/1.1的区别【高频】

第一个是 长连接&#xff1a; HTTP/1.0 默认 短连接&#xff0c;&#xff08;它也可以指定 Connection 首部字段的值为 Keep-Alive实现 长连接&#xff09;而HTTP/1.1 默认支持 长连接&#xff0c;HTTP/1.1是基于 TCP/IP协议的&#xff0c;创建一个TCP连接是需要经过三次握手的…...

Rk3568驱动开发_开发环境的搭建_1

1、环境说明&#xff1a; 需要用官方的程序包&#xff0c;这个程序需要在虚拟机里编译再将镜像烧录到板子里&#xff0c;本质上是给板子上一套Linux操作系统&#xff0c;镜像是.img文件 Linux操作系统被分成了多个模块&#xff0c;编译好后储存在镜像里&#xff0c;本质上就和…...

Solr中得Core和Collection的作用和关系

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

Visual Studio Code 远程开发方法

方法1 共享屏幕远程控制&#xff0c;如 to desk, 向日葵 &#xff0c;像素太差&#xff0c;放弃 方法2 内网穿透 ssh 第二个方法又很麻烦&#xff0c;尤其是对于 windows 电脑&#xff0c;要使用 ssh 还需要额外安装杂七杂八的东西&#xff1b;并且内网穿透服务提供商提供的…...

如何看到 git 上打 tag 的时间

在 Git 中可以通过以下方法查看标签&#xff08;tag&#xff09;的创建时间&#xff1a; 使用 git show 命令&#xff1a; 运行以下命令可以查看某个特定标签的详细信息&#xff0c;包括创建时间&#xff1a; git show 输出中会包含 Tagger 的信息和 Date 字段&#xff0c;显示…...

【HarmonyOS Next】鸿蒙TaskPool和Worker详解 (一)

【HarmonyOS Next】鸿蒙TaskPool和Worker详解 &#xff08;一&#xff09; 一、TaskPool和Worker如何实现多线程&#xff1f;各自特点是什么&#xff1f; 在鸿蒙中通过TaskPool和Worker实现多线程并发&#xff0c;两者都基于Actor并发模型实现。 Actor并发模型&#xff0c;每…...

如何设置HTTPOnly和Secure Cookie标志?

设置HttpOnly和Secure标志于Cookie中是增强Web应用安全性的重要措施。这两个标志帮助防止跨站脚本攻击&#xff08;XSS&#xff09;和中间人攻击&#xff08;MitM&#xff09;。下面是关于如何设置这些标志的具体步骤&#xff1a; 设置方法 在服务器端设置 根据你的服务器端…...

几个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 安装和使用模型&#xff0c;您可以按照以下步骤进行操作&#xff1a; 步骤 1&#xff1a;安装 Ollama 安装 Ollama&#xff1a; 使用以下命令安装 Ollama&#xff1a; curl -sSfL https://ollama.com/install.s…...

基于 DeepSeek+AutoGen 的智能体协作系统

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

博客系统笔记总结 2( Linux 相关)

Linux 基本使用和程序部署 基本命令 文件操作 显示当前目录下的文件 ls&#xff1a;显示当前目录下的文件 ll&#xff1a;以列表的形式展示&#xff0c;包括隐藏文件 进入目录 && 显示当前路径 cd&#xff1a;进入目录&#xff08;后面跟相对路径或者绝对路径&…...

计算机毕业设计SpringBoot+Vue.js电影评论网站系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

精美登录注册UI,登录页面设计模板

精美登录注册UI,登录页面设计模板 引言 在网页设计中,按钮是用户交互的重要元素之一。一个炫酷的按钮特效不仅能提升用户体验,还能为网页增添独特的视觉吸引力。今天,我们将通过CSS和JavaScript来实现一个“精美登录注册UI,登录页面设计模板”。该素材呈现了数据符号排版…...

《Linux系统编程篇》共享内存(Linux 进程间通信(IPC))——基础篇

文章目录 引言什么是共享内存System V 共享内存 API 引入1. shmget2. shmat3. shmdt4. shmctl5. 结构体 shmid_ds 开始实操注意 结束 今天的你有没有前进一小步呢 ——家驹(StrangeHead) 引言 那么共享内存&#xff0c;我们如何去使用他呢&#xff0c;先来听笔者啰嗦一段话吧…...

【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语言的特点&#xff0c;以下是一个系统化的学习路径&#xff0c;适合从初学者到进阶者&#xff1a; 第一阶段&#xff1a;Linux基础 Linux操作系统基础 学习Linux基本命令&#xff1a;ls、cd、mkdir、rm、grep、find等。 理解…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...