【数据结构】二叉树——顺序结构——堆及其实现

一、树

        1.1、树的概念和结构

树是一种非线性的数据结构,它是由n(n>=0)个有限节点组成的一个具有层次关系的集合。

        树有一个特殊的节点,称为根节点,根节点没有前驱结点。

        除根节点外,其余部分被分为M(M>0)个互不相交的集合T1、T2、.....Tm,其中的每一个集合 Ti(1 <= i <= m)又是一颗结构与数类似的子树。每一颗子树的根节点都有且只有一个前驱节点,而可以有0个或者多个后继节点。

        树是递归定义的。

在树形结构中,子树之间不能存在交集

        

子树中存在交集,就不是树形结构了

除了根节点以外,每一个节点有且只有一个父节点

如图,E节点存在两个父节点,该图不是树形结构。

一颗N个节点的树有 N-1条边

        1.2、树的相关术语

在树形结构中,有一些相关术语:

        父节点(双亲节点):如果一个节点含有子节点,则这个节点称为其子节点的父节点;如上图:A就是B的父节点。

        子节点:一个节点含有的子树的根节点称为该节点的子节点;如上图:B是A的子节点。

        节点的度:一个节点存在几个子节点,它的度就是多少;如上图:A的度为6、F的度为2、K的度为0.

        树的度:在一个树形结构中,最大的节点的度,称为树的度;如上图:树的度为6。

        叶子结点(终端节点):度为0的节点就是叶子节点;简单来说,就是没有子节点(下一个节点);如上图:B、C、H、I 等节点都是叶子结点。

        分支节点:度不为0的节点称为分支节点;如上图:D、E、F、G 等节点都是分支节点。

        兄弟节点:具有相同的父节点的节点称为兄弟节点;如上图:B和C是 兄弟节点。

        节点的层次:从树形结构的跟开始定义,跟为第 1 层,跟的子节点为第 2 层,以此类推。

        树的高度:树中节点的最大层次;如上图:树的高度是 4 。

        节点的祖先:从根到该节点所经过的分支上的所以节点。如上图:A是所有节点的祖先。

        路径:一条从树的任意节点出发,沿父节点-子节点连接,到达任意节点的序列。比如上图中A到Q的路径:A-E-J-Q。

        子孙:以某节点为根的子树中任一节点都称为该节点的子孙。

        森林:右m(m>0)个互不相交的树组成的集合称为森林。

        1.3、树的表示

        树的表示相对于线性表就复杂了,想要存储表示起来就很麻烦了,这里既要保存值域,也要保存节点和节点之间的关系;树有很多中表示方法,就比如:双亲表示法,孩子表示法,孩子双亲表示法以及孩子兄弟表示法等

        这里看一下简单的孩子兄弟表示法

struct TreeNode
{struct Node* child; // 左边开始的第⼀个孩⼦结点struct Node* brother; // 指向其右边的下⼀个兄弟结点int data; // 结点中的数据域
};

           

    这样的一个树就可以表示成下面这种形式

        1.4、树形结构的分类和应用

树形结构分为很多种,具体如上图,

        树形结构实际应用:

        最典型的就是,计算机存储和管理文件的文件系统。它利用树形结构来组织和管理文件和文件夹。再文件系统中,树结构被广泛利用。通过父节点和子节点之间的关系来表示不同层级的文件和文件夹之间的关系

二、二叉树

        2.1、二叉树的概念与结构

        二叉树是树形结构的一种。

        根据上图,我们不难看出二叉树具备以下特点:

  • 二叉树不存在度大于 2  的节点 
  • 二叉树的子树有左右之分,次序不能颠倒,因此,二叉树是有序树。

        2.2、特殊的二叉树

2.2.1、满二叉树

        一个二叉树,如果每一个层的节点数都达到最大值,则这个二叉树就是满二叉树。也就是,如果二叉树的层数为k,且节点总数为2^k - 1,则它就是满二叉树。

2.2.2、完全二叉树

        完全二叉树是效率很高的 数据结构,完全二叉树是由满二叉树而引出来的。对于深度为k的,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时称之为完全 二叉树。要注意的是,满二叉树是一种特殊的二叉树。

2.2.3、二叉树的存储结构

        二叉树,我们可以使用两种结构存储;一种是顺序结构,另外一种就是链式结构。

顺序结构

        顺序结构,就是使用数组来存储;而数组一般只适合表示完全二叉树(如果用数组表示不完全二叉树,就会导致空间的浪费)。所以对于完全二叉树来说,就更适合使用顺序结构存储。

        我们通常使用堆(一种二叉树(数据结构))顺序结构的数组来存储二叉树顺序结构(完全二叉树)

链式结构

        对于链式结构,本篇不做详解;在下一篇文章会详细讲解二叉树的链式结构。

三、二叉树顺序结构——堆及其实现

        3.1、堆的概念

        堆是一种满足特定条件的完全二叉树,主要存在以下两种结构

小顶堆:任意节点的值 <= 其子节点的值

大顶堆:任意节点的值 >= 其子节点的值

根据图,我们可以看出堆具有一些特性:

  • 最底层的节点靠左,其余层的节点都被填满

  • 二叉树的根节点称为“堆顶”,底部最右边的节点称为“根节点”
  • 大堆的堆顶元素的值是最大的;小堆的堆顶元素的值是最小的

这里补充:

        对于堆这样的数据结构存储二叉树:

对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从
0 开始编号,则对于序号为 i 的结点有:
  1. 若 i>0 , i 位置结点的双亲序号: (i-1)/2 ; i = 0 , i 为根结点编号,无父节点
  2. 若 2i+1<n ,左孩子序号: 2i+1 , 2i+1>=n 否则无左孩子节点
  3. 若 2i+2<n ,右孩子序号: 2i+2 , 2i+2>=n 否则无有孩子节点

        3.2、堆的实现

        现在来实现这样一个堆结构。

堆的底层结构是一个数组,定义堆的结构

3.2.1、堆的结构

//堆的结构
typedef int HPDataType;
typedef struct HeapNode
{HPDataType* arr;int size; //有效数据个数int num;  //空间大小
}HP;

3.2.2、初始化和销毁

        这里堆的底层结构是数组,初始化和销毁与之前顺序表基本一样。

//初始化
void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->num = 0;
}
//销毁
void HPDesTroy(HP* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->num = 0;
}

3.3.3、判断堆是否为空

        判断堆是否为空,直接判断堆中数据个数是否为0即可。

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

3.3.4、求堆中数据的个数

        因为堆结构中的size成员就是堆中数据的个数,直接返回。

//求数据个数size
int HPSize(HP* php)
{assert(php);return php->size;
}

3.3.5、插入数据

        这里向堆中插入数据,我们还要保证堆的结构不被破坏,就只好将数据插入堆之后,再调整数据(这里因为是在堆底(也就是数组的最后)插入的数据,我们就使用向上调整算法)

void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}
//向上调整算法
void AdjustUp(HPDataType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;while (child > 0){//小堆if (arr[parent] > arr[child]){Swap(&arr[parent], &arr[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
//插入数据
void HPPush(HP* php, HPDataType x)
{assert(php);//判断空间够不够if (php->num <= php->num){int newnum = (php->num == 0) ? 4 : 2 * php->num;HPDataType* tmp = (HPDataType*)realloc(php->arr, newnum * sizeof(HPDataType));if (tmp == NULL){perror("realloc filed");exit(1);}php->arr = tmp;php->num = newnum;}//空间足够,插入数据php->arr[php->size++] = x;//调整堆结构AdjustUp(php->arr, php->size - 1);
}

3.3.6、堆的向上调整算法

        堆的向上调整算法,这里以小堆为例

将新的数据插入到数组的尾上,我们用向上调整,直到满足堆的结构

        将元素插入到堆的末尾之后,再进行向上调整
        出入数据之后,如果不满足堆的结构(小堆就是,子节点大于父节点了),就将插入节点顺着父节点向上进行调整即可。

void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}
//向上调整算法
void AdjustUp(HPDataType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;while (child > 0){//小堆if (arr[parent] > arr[child]){Swap(&arr[parent], &arr[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

3.3.7、删除数据

        删除数据,这里删除的是堆的堆顶数据,数据在删除后,堆结构可能被破坏,这里也需要进行调整,使用的是向下调整算法。

删除数据,先将堆顶数据和堆底数据进行调换,再从堆顶开始往下调整

//删除数据
void HPPop(HP* php)
{assert(php);Swap(&php->arr[0], &php->arr[php->size - 1]);php->size--;AdjustDown(php->arr, 0, php->size);
}

3.3.8、堆的向下调整算法

        向下调整算法,以小堆为例

如果该节点的值大于子节点,就向下调整,直到结束(结束条件,遍历完数组或者堆里的数据满足堆结构了)

void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}
//向下调整算法
void AdjustDown(HPDataType* arr, int parent, int n)
{assert(arr);int child = parent * 2 + 1;while (child < n){//找到两个子节点中小的节点if (child<n - 1 && arr[child]>arr[child + 1]){child++;}if (arr[child] < arr[parent]){Swap(&arr[parent], &arr[child]);parent = child;child = 2 * parent + 1;}else{break;}}
}

到这里,堆的基本操作就实现完了。

        3.3、堆的实际应用

3.3.1、堆排序

堆的一个应用就是堆排序,这里简单介绍一下,后面会有详细理解

        对于堆排序,我们可以基于本篇实现的堆结构来实现堆排序

如果要排升序,就建大堆;排降序,就排小堆。

基于堆结构来进行堆排序

void HeapSort(int* a, int n)
{// a数组直接建堆 for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}

        这样来实现,时间复杂度为O(n*log n)。

当然,我们也可以脱离这样的堆结构,利用堆这种思想来实现堆排序,这个在后面有详细介绍。

3.3.2、TOP-K问题

        TOP-K 问题:求数据集合中前K个最大的元素或者最小的元素,(一般这样的数据量特别的大)。

而对于这种问题,能想到的最简单最直接的方法就是排序,而数据量如果很大很大,排序不可取了(数据不能一下子全部加载到内存中)。而堆就可以来解决这样的问题。

思路:

1> 取数据集合的前k个元素来建堆

        如果需要前k个最大的元素,就建小堆

        如果需要前k个最小的元素,就建大堆

2> 用剩余的数据依次和堆顶元素进行比较,如果不满足条件,就替换堆顶元素

        将剩余的元素依次和堆顶元素比较完之后,堆中剩余的k个元素就是所求的前k个最小或者最大的元素

这里简单实现一下这样的TOP-K问题

void CreateNDate()
{// 造数据int n = 100000;srand(time(0));const char* file = "data.txt";FILE * fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = (rand() + i) % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}
void TOPk()
{int k = 0;printf("请输入k:");scanf("%d", &k);const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen fail!");exit(1);}int* minHeap = (int*)malloc(k * sizeof(int));if (minHeap == NULL){perror("malloc fail!");exit(2);}//从文件中读取前K个数据for (int i = 0; i < k; i++){fscanf(fout, "%d", &minHeap[i]);}//建堆---小堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(minHeap, i, k);}int x = 0;while (fscanf(fout, "%d", &x) != EOF){//读取到的数据跟堆顶的数据进行比较//比堆顶值大,交换入堆if (x > minHeap[0]){minHeap[0] = x;AdjustDown(minHeap, 0, k);}}for (int i = 0; i < k; i++){printf("%d ", minHeap[i]);}fclose(fout);
}

假设现在我们要从这十万个数据里取5个最大的数据

        先随机生成十万个数据存储到文件data.txt中,我们再设置一下这5个最大的数据

现在这5个数据是最大的(其余的数据都小于100000)

运行看一下结果这里正是这5个数据。

感谢各位大佬支持并指出问题,

                        如果本篇内容对你有帮助,可以一键三连支持以下,感谢支持!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/49404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海康视频WEB插件

引入相关依赖 index.html <script src"/video/web-control_1.2.5.min.js"></script> <script src"/video/jquery-1.12.4.min.js" type"text/javascript"></script> <script src"/video/jsencrypt.min.js" …

[UE 虚幻引擎] DTHmacSha 蓝图HMACSHA加密算法插件说明

本插件可以在虚幻引擎中使用蓝图对字符串和文件进行HMACSHA加密。 1.节点说明 HMACSHA一共有5种加密方式&#xff0c;分辨是 HMAC SHA-1&#xff0c; HMAC SHA-224&#xff0c;HMAC SHA-256&#xff0c;HMAC SHA-384&#xff0c;HMAC SHA-512。 本插件对每种加密方式提供3个节点…

快自查一下,你的手机有没有被乱扣费吧!查询方法都告诉你了!

不知道你有没有这种困扰&#xff0c;明明办的是29元的套餐&#xff0c;但是每月扣费的时候总是莫名其妙的被多收&#xff0c;如果你也有这种情况&#xff0c;那么有可能是被乱扣费了&#xff0c;下面这篇文章教你如何自查是否被乱扣费以及解决方法&#xff0c;大家可以参考。 ​…

npm上传自己的包以及发布过程遇到的问题

大家好&#xff0c;我是前端追寻路上的【酱酱仔】 作为在前端领域不断探索的一员&#xff0c;在此记录开发中遇到的问题&#xff0c;如果你也遇到了相同的问题&#xff0c;希望本文对你有帮助。 前提&#xff1a;本文涉及的命令都是在要发布的包的根目录下执行的&#xff0c;在…

unity文字||图片模糊

一.文字模糊 1、增大字体大小后等比缩放 快捷键R 2、更改字体渲染模式 二.图片模糊 1、更改过滤模式 2、更改格式或者压缩 3、如果只是图片边缘看不清&#xff0c;可以增加canvas/图片的每单位参考像素

nginx漏洞修复 ngx_http_mp4_module漏洞(CVE-2022-41742)【低可信】 nginx版本升级

风险描述&#xff1a; Nginx 是一款轻量级的Web服务器、反向代理服务器。 Nginx 的受影响版本中的ngx _http_mp4_module模块存在内存越界写入漏洞&#xff0c;当在配置中使用 mp4 directive时&#xff0c;攻击者可利用此漏洞使用使用ngx_http_mp4_module模块处理特制的音频或视…

WARNING: The Nouveau kernel driver is currently in use by your system. 处理方法

实践系统&#xff1a; 安装NVIDIA驱动时&#xff0c;提示&#xff1a; WARNING: The Nouveau kernel driver is currently in use by your system. This driver is incompatible with the NVIDIA driver&#xff0c;and must be disabled before proceeding.警告&#xff1…

Meta发布Llama 3.1开源大语言模型;谷歌发布NeuralGCM AI天气预测模型

&#x1f989; AI新闻 &#x1f680; Meta发布Llama 3.1开源大语言模型 摘要&#xff1a;Meta正式发布了开源大语言模型Llama 3.1&#xff0c;包括8B、70B和405B参数版本。Llama 3.1在推理能力和多语言支持方面有所改进&#xff0c;上下文长度提升至128K&#xff0c;405B参数…

node和npm安装;electron、 electron-builder安装

1、node和npm安装 参考&#xff1a; https://blog.csdn.net/sw150811426/article/details/137147783 下载&#xff1a; https://nodejs.org/dist/v20.15.1/ 安装&#xff1a; 点击下载msi直接运行安装 安装完直接cmd打开可以&#xff0c;默认安装就已经添加了环境变量&…

科技引领水资源管理新篇章:深入剖析智慧水利解决方案,展现其在提升水资源利用效率、优化水环境管理方面的创新实践

本文关键词&#xff1a;智慧水利、智慧水利工程、智慧水利发展前景、智慧水利技术、智慧水利信息化系统、智慧水利解决方案、数字水利和智慧水利、数字水利工程、数字水利建设、数字水利概念、人水和协、智慧水库、智慧水库管理平台、智慧水库建设方案、智慧水库解决方案、智慧…

JavaDS —— 排序

排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&a…

人大金仓亮相国际金融展,助力数字金融跑出“加速度”

7月19日至21日&#xff0c;由商务部批准、中国金融电子化集团有限公司主办的2024中国国际金融展&#xff08;以下简称“金融展”&#xff09;在北京国家会议中心举办。作为数据库领域国家队&#xff0c;人大金仓携金融领域创新成果与解决方案亮相本次金融展&#xff0c;获得了业…

亚信安全与软银中国全资企业爱思比通信达成战略合作

近日&#xff0c;亚信安全携手软银集团旗下全资企业爱思比通信科技&#xff08;上海&#xff09;有限公司&#xff08;以下简称“爱思比通信”&#xff09; 共同宣布&#xff0c;双方正式签署战略合作协议。依托双方在技术、业务和资源三大层面的实力与优势&#xff0c;亚信安全…

学习在测试时学习(Learning at Test Time): 具有表达性隐藏状态的循环神经网络(RNNs)

摘要 https://arxiv.org/pdf/2407.04620 自注意力机制在长文本语境中表现良好&#xff0c;但其复杂度为二次方。现有的循环神经网络&#xff08;RNN&#xff09;层具有线性复杂度&#xff0c;但其在长文本语境中的性能受到隐藏状态表达能力的限制。我们提出了一种新的序列建模…

controller层-请求格式为json-请求方法为get

前置条件 get请求映射&#xff0c;内容和PostMapping一致&#xff0c;需要请求参数更换为get数据 请求过程&#xff1a;用户请求--初始化DispatcherServlet及对接和分发用户请求--controller--service 用户请求&#xff1a;http://ip:port/user/getinfo 请求方法&#xff1a;ge…

redis全局唯一ID生成策略、countDownLatch、Lambda表达式总结

redis全局唯一ID生成策略 一、有哪些生成全局唯一ID的策略二、使用Redis自增1. 分析2. RedisIdWorker配置类3 单元测试注解分析&#xff08;难点较多&#xff09;3.1 countDownLatch前言3.2 常用方法 一、有哪些生成全局唯一ID的策略 二、使用Redis自增 1. 分析 2. RedisIdWor…

Java查询ES报错 I/O 异常解决方法: Request cannot be executed; I/O reactor status: STOPPED

问题 ES Request cannot be executed; I/O reactor status: STOPPED 报错解决 在使用ES和SpringBoot进行数据检索时&#xff0c;在接口中第一次搜索正常。第二次在搜索时在控制台就会输出Request cannot be executed; I/O reactor status: STOPPED错误 原因 本文错误是因为在使…

大语言模型-GPT-Generative Pre-Training

一、背景信息&#xff1a; GPT是2018 年 6 月由OpenAI 提出的预训练语言模型。 GPT可以应用于复杂的NLP任务中&#xff0c;例如文章生成&#xff0c;代码生成&#xff0c;机器翻译&#xff0c;问答对话等。 GPT也采用两阶段的训练过程&#xff0c;第一阶段是无监督的方式来预训…

7.23 字符串简单中等 520 125 14 34

520 Detect Capital 思路&#xff1a; 题目&#xff1a;判定word &#xff1a;if the usage of capitals in it is right.遍历所有的string&#xff1a; 两种情况&#xff1a; 首字母capitals–>判定第二个字母是否大写–>所有字母大写 otherwise 除第一个以外全部小写&a…

nginx的配置和使用

一、nginx支持win和linux版本的下载&#xff0c;选择合适的版本进行安装 二、配置文件注解 重点的几个参数进行注释&#xff1a; 1、listen 要监听的服务的端口&#xff0c;符合这个端口的才会被监听 server_name要监听的服务地址&#xff0c;可能是ip,也可能是域名&#xf…