【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力

在这里插入图片描述

初阶数据结构相关知识点可以通过点击以下链接进行学习一起加油!
时间与空间复杂度的深度剖析深入解析顺序表:探索底层逻辑深入解析单链表:探索底层逻辑深入解析带头双向循环链表:探索底层逻辑深入解析栈:探索底层逻辑
深入解析队列:探索底层逻辑深入解析循环队列:探索底层逻辑树与二叉树:从零开始的奇幻之旅

本篇将介绍堆的相关操作与应用,在堆应用方面包括堆排序和Tok问题的解法措施,分析向上向下调整算法对堆进行的调整,利用父子节点之间的规律,帮助我们更好地学习完全二叉树的独特魅力和掌握特殊的完全二叉树堆相关接口的实现

请添加图片描述
Alt

🌈个人主页:是店小二呀
🌈C语言笔记专栏:C语言笔记
🌈C++笔记专栏: C++笔记
🌈初阶数据结构笔记专栏: 初阶数据结构笔记

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅
请添加图片描述

文章目录

  • 一、二叉树的顺序结构
  • 二、堆的概念及结构
  • 三、堆的实现
    • 3.1 堆向下调整算法
    • 3.2 向上调整算法
    • 3.3 处理完全二叉树不是堆情况
    • 3.4 堆的插入
    • 3.5 堆的删除
      • 3.5.1 挪移数据覆盖删除
      • 3.5.2 首尾交换再删除
  • 四、堆的应用
    • 4.1 堆排序
      • 4.1.1 如果升序建小堆
      • 4.1.2 向上或向下调整建堆
      • 4.1.3向下向上调整建堆时间复杂度
    • 4.2 TOP-K问题

一、二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段

在这里插入图片描述

二、堆的概念及结构

如果有一个关键码的集合K ={k0,k1,k2,…,kn-1},把它的所有元素按照完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2*i+1且 Ki <= K2*i+2 (Ki >= K2*i+1且 Ki >= K2*i+2) i =0, 1, 2…则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆

堆分为大堆和小堆

  • 小堆要求:任意一个父亲结点<=孩子结点

  • 大堆要求:任意一个父亲结点>=孩子结点

堆的性质

  • 大堆中某个节点的值总是不大于其父节点的值
  • 小堆中某个节点的值总是不小于其父节点的值
  • 堆总是一棵完全二叉树

在这里插入图片描述

三、堆的实现

堆分为大堆或小堆,无论是向上或向下调整算法,会根据大小堆的需求去修改部分的代码,其实就是修改大于小于号的问题。以下代码部分是根据建小堆来走,如果需要建大堆可以修改直接的大于小于号。

堆总是一颗完全二叉树,对于搭建完全二叉树的结构,一般采用数组作为存储结构,而完全二叉树作为逻辑结构。

父子节点间下标规律关系

  • leftchild = parent * 2 + 1;

  • rightchild = paretn * 2 +2;

  • parent = (child - 1) / 2;(不区分左右孩子)

3.1 堆向下调整算法

堆向下调整(Heapify Down)是一个修复堆性质的过程,而不是用于初始化或完全建立堆数据结构的过程。使用向下调整算法的前提是需要左右子树必须是一个堆才能进行调正,如果左右子树不是一个堆,我们将不采取使用向下调整算法,而是采用向上调整算法。

堆向下调整算法只用于根节点不满某种条件时,使用向下调正算法进行调整,至于使用向下调整算法不能达到我们的预期,比如现在建小堆,从根节点和根左右节点调整,由于左右子树不是一个小堆,无法保证此时的根就是最小的值,可能在某个子树中,左右子树话没有进行调整。除此之外删除节点也适合向下调整算法。

在这里插入图片描述

void AdjustDown(HPDataType *a,int size,int parent)
{int child=parent * 2 + 1;while(child<size)//空树或者只有一个结点{//假设左孩子小,如果右孩子小,就更新下(左右孩子相差1)选择较小的孩子if(child+1<size && a[child+1]<a[child]){++child;}if(a[child]<a[parent]){Swap(&a[child],&a[parent]);//通过孩子结点的数值赋值父亲结点,实现向下的逻辑parent=child;child=parent*2+1;              }else{break;}}
} 

3.2 向上调整算法

在堆数据结构中,堆向上调整(Heapify Up)是一种用于保持堆的性质的操作,通常适用于最后一个元素出现问题或者插入新元素的时候使用.

void AdJustUp(HPDataType *a,int child)
{int parent=(child-1)/2;//父亲和孩子的数值是下标while(child>0)//向上到根就停下{if(a[child] < a[parent]){swap(&a[child],&a[parent]);//通过父亲结点的数值赋值孩子结点,实现向上的逻辑child=parent;parent=(child-1)/2;}else{break;//不用交换,直接退出}}   
}

这里的孩子节点不用需要判断左右孩子,肯定是先插入左孩子,在插入右孩子,如果如果是插入右孩子,就不必要考虑左孩子,此时左孩子的值是符合大于(小于)等于父亲的情况.

无论是向下调整算法还是向下调整算法,目的都是使得保持堆的性质,在判断语句中得以体现。想要更好地理解这个两个算法,搞清楚谁是需要被处理的节点,循环条件是什么?

3.3 处理完全二叉树不是堆情况

把它构建成一个堆。根节点左右子树不是堆,这里我们从倒数的第一个非叶子节点的子树使用向上调整算法开始调整,一直调整到根节点的树,就可以调整成堆。

在这里插入图片描述

3.4 堆的插入

随机插入一个数值到数值的尾上,再进行向上调整算法直到满足堆

在这里插入图片描述

void HeapPush(HP *php,HPDataType x)
{assert(php);if(php->size==php->capacity){int newcapacity=php->capacity==0?4:php->capacity*2;HPDataType*tmp=(HPDataType*)realloc(php->a,newcapacity*sizeof(HPDataTyped));if(tmp==NULL){perror("realloc fail!!!");return 1;}php->a=tmp;php->capacity=newcapacity;}php->a[php->size]=x;php->size++;//重头戏--向上调整形成一个堆,这里的size代表的是下一个元素,所以-1AdjustUp(php->a,php->size-1);
}

3.5 堆的删除

关于堆的删除,我们一般默认规定删除堆顶也是就是根节点,至于删除尾部数据意义不大,尾部数据没有特别的地方,既不是最大(小)的数据意义不大。

3.5.1 挪移数据覆盖删除

挪移数据覆盖会导致堆发生严重BUG,整棵树的父子关系全乱,也就是需要维持大小关系乱了(我拿你当兄弟,你却像当我爹)

在这里插入图片描述

3.5.2 首尾交换再删除

对于堆的删除,我们采用另外一种方法,首尾交换再删除,左右子树依旧是堆,同时关系也没有乱,并且删除堆顶数据通过尾删再向下调整代价很低

在这里插入图片描述

void HeapPop(HP *php)
{assert(php);assert(php->size>0)//没有数值,不需要删除Swap(&php->a[0],&php->a[php->size-1]);//size是指向下一个php->size--;AdjustDown(php->a,php->size,0)}

四、堆的应用

4.1 堆排序

堆排序(HeapSort)移除位在第一个数据的根节点,并做最大堆调整的递归运算建堆(本质:模拟堆插入的过程建堆)。上面对于堆的调整不是叫做堆排序,堆排序是对数组元素进行操作的

堆排序即是运用堆的思想进行排序,总共分为两个步骤:建堆和利用堆删除思想进行排序

1.建堆 (后面有解释)

  • 升序:建大堆
  • 降序:建小堆

2.利用堆删除思想来进行排序

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

在这里插入图片描述

该过程解析:这里是需要升序,根据结论需要建大堆。可以这样子理解升序建大堆目的,我们配合物理结构数组和逻辑结构二叉树去看待这个问题,如果我们需要升序,意味着数组最后一个元素是最大值,那么大堆可以保证堆顶元素是最大值,再利用堆删除的思想,将堆顶元素和尾元素交换,那么可以保证最大值在尾,而且由于是大堆,尾元素互会通过向下调整算法使得堆顶元素为次大的值,这个时候最后一个元素不用去动他,倒数第二个位置跟次大堆顶元素交换,这样子就完成了堆排序。

4.1.1 如果升序建小堆

如果升序建小堆1 2 2 6 5 8 4 9,当我们把最小值1选出来后,接下来需要找次小值。最小值1的位置是不动,剩下的数不能看成堆,关系乱了,只能重新建堆,找出次小值,但是代价很大

在这里插入图片描述

4.1.2 向上或向下调整建堆

这里为了快速地使用堆排序,这里可以直接通过向上或向下调整算法直接建堆。不止可以使用向上调整建堆,也可以使用向下调整建堆(使用向下调整建堆,需要保证左右为堆),对此不能从整体入手,可以一步步向上。从倒数的第一个非叶子,也就是最后一个结点的父亲,不断的向上而向下调整。向下调整建堆相较于向上调整建堆有很多优势,在建堆的时间复杂度分析中,可以看出,这里关于这方面会单独拿出来分析。对此这需要掌握堆向下调整算法即可

这里不要跟上面堆的插入混淆,这里数组元素已经确定,而堆的插入元素在不断地更新,如果使用向下调整意味着从新插入界节点重新向上调整,向上调整只需要对新插入节点进行移动即可

//升序void HeapSort(int *a,int n)  //O(N*logN)//for(int i=0;i<n;i++)//{//  AdjustUp(a,i);// }//O(N)for(int i=(n-1-1)/2;i>=0;--i){AdjustDown(a,n,i);//从倒数的第一个非叶子,也就是最后一个结点的父亲}int end=n-1;//下标--同时调整后,最后一个元素不再改动//O(N*logN)while(end>0)//利用堆删除思想进行排序{Swap(&a[0],&a[end]);AdjustDown(a,end,0);//要清楚为什么要向下调整--end;}

4.1.3向下向上调整建堆时间复杂度

在这里插入图片描述

在这里插入图片描述

过程解析:无论是向上还是向下调整建堆,建堆的累积调整次数等于每一层节点个数*向上(下)调正次数之和。主要是利用高中数学中错位相减法计算出求和通式。由于一般不会得知树的高度去求时间复杂度,而是通过节点个数去求时间复杂度,这里需要利用树的高度与节点个数的关系式,进行替换即可满二叉树:2^h-1 =N 可得 h =log(N+1)

这里建堆主要就是受到每一个节点个数*向上(下)调正次数,对于向下调整建堆多节点*少调整、少节点*多调整,而向上调整建堆多节点*多调整、少节点*少调整导致时间复杂度差异。

4.2 TOP-K问题

即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中,内存不足的问题)。最佳的方式就是用堆来解决,基本思路如下

数据集合中前K个元素来建堆

  • 前k个最大的元素,则建小堆
  • 前k个最小的元素,则建大堆

用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素 ,将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素 ,时间复杂度O(N*logK)
在这里插入图片描述

解析过程:这里思路跟堆排序大差不差,主要就是利用堆顶的特性。如果需要找出最大值,那么在小堆(都是很大的值)中堆顶就相当于门槛,至少需要被最大中的最小要大才有资格进来,然后重新筛选出来新的最小值当保安。

测试代码(自取)

void TestTopk()
{int n = 10000;int* a = (int*)malloc(sizeof(int)*n);srand(time(0));for (size_t i = 0; i < n; ++i){a[i] = rand() % 1000000;}a[5] = 1000000 + 1;a[1231] = 1000000 + 2;a[531] = 1000000 + 3;a[5121] = 1000000 + 4;a[115] = 1000000 + 5;a[2335] = 1000000 + 6;a[9999] = 1000000 + 7;a[76] = 1000000 + 8;a[423] = 1000000 + 9;a[3144] = 1000000 + 10;
}

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二初阶数据结构笔记,希望对你在学习初阶数据结构中有所帮助!
请添加图片描述

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

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

相关文章

Qt类 | QObject类详解

文章目录 一、QObject类介绍二、Properties&#xff08;属性&#xff09;三、Public Functions&#xff08;公共函数&#xff09;四、Public Slots&#xff08;公共槽函数&#xff09;五、Signals&#xff08;信号&#xff09;六、Static Public Members&#xff08;静态公共成…

react Ant Design 动态表头添加操作列

模拟后端返回的表头、列表数据 注意&#xff1a;我们要在表头数据中添加一个 render 函数&#xff0c;里面就是你操作列的内容&#xff0c;value是你数据列表每行的对象 &#xff0c;item 是你表头的对象 页面中去处理这个两个数组 dataList.forEach((item, index) > {item.…

昇思25天学习打卡营第24天 | RNN实现情感分类

概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film is great 正确标…

dmasmtool工具详细用法

DMASMTOOL 是 DMASM 文件系统管理工具&#xff0c;提供了一套类 Linux 文件操作命令&#xff0c;用于管理 ASM 文件&#xff0c;是管理、维护 DMASM 的好帮手。DMASMTOOL 工具使用 DMASMAPI 连接到 DMASMSVR&#xff0c;并调用相应的 DMASMAPI 函数&#xff0c;实现创建、拷贝、…

C1W2.LAB.Visualizing Naive Bayes

理论课&#xff1a;C1W2.Sentiment Analysis with Nave Bayes 文章目录 导入包Calculate the likelihoods for each tweetUsing Confidence Ellipses to interpret Nave Bayes 理论课&#xff1a; C1W2.Sentiment Analysis with Nave Bayes 导入包 在下面的练习中&#xff0…

everything搜索不到任何文件-设置

版本&#xff1a; V1.4.1.1024 (x64) 问题&#xff1a;搜索不到任何文件 click:[工具]->[选项]->下图所示 将本地磁盘都选中包含

Python爬虫速成之路(3):下载图片

hello hello~ &#xff0c;这里是绝命Coding——老白~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff1a;绝命Coding-CSDN博客 &a…

SpringMVC源码解析(一):web容器启动流程

SpringMVC源码系列文章 SpringMVC源码解析(一)&#xff1a;web容器启动流程 目录 一、SpringMVC全注解配置1、pom文件2、web容器初始化类(代替web.xml)3、SpringMVC配置类(代替springmvc.xml)4、测试Controller 二、SpringServletContainerInitializer1、web容器初始化入口2、…

从本地到全局:基于图的RAG方法进行查询聚焦原理摘要

摘要 使用检索增强生成&#xff08;RAG&#xff09;从外部知识源检索相关信息&#xff0c;使大型语言模型&#xff08;LLMs&#xff09;能够回答有关私有和/或以前未见过的文档集合的问题。然而&#xff0c;当针对整个文本文档库提出全局问题时&#xff0c;例如“数据集中的主…

音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现

一、引言 通过FFmpeg命令可以获取到H.264裸流文件的色彩格式&#xff08;又译作色度采样结构、像素格式&#xff09;&#xff1a; 在vlc中也可以获取到色彩格式&#xff08;vlc底层也使用了FFmpeg进行解码&#xff09;&#xff1a; 这个色彩格式就是之前的文章《音视频入门基础…

【精品资料】模块化数据中心解决方案(33页PPT)

引言&#xff1a;模块化数据中心解决方案是一种创新的数据中心设计和部署策略&#xff0c;旨在提高数据中心的灵活性、可扩展性和效率。这种方案通过将数据中心的基础设施、计算、存储和网络资源封装到标准化的模块中&#xff0c;实现了快速部署、易于管理和高效运维的目标 方案…

2024最新Cloudways主机使用教程(含最新Cloudways折扣码)

Cloudways是一家提供云托管服务的公司&#xff0c;可以帮助你轻松管理和运行你的网站。本教程是Cloudways主机注册和使用教程。Cloudways界面简洁&#xff0c;使用方便&#xff0c;不需要复杂的设置&#xff0c;就能快速搭建一个WordPress网站。它的主机功能包括高级缓存和Bree…

DepthAnything(2): 基于ONNXRuntime在ARM(aarch64)平台部署DepthAnything

DepthAnything(1): 先跑一跑Depth Anything_depth anything离线怎么跑-CSDN博客 目录 1. 写在前面 2. 安装推理组件 3. 生成ONNX 4. 准备ONNXRuntime库 5. API介绍 6. 例程 1. 写在前面 DepthAnything是一种能在任何情况下处理任何图像的简单却又强大的深度估计模型。 …

KingbaseES数据库逻辑备份还原

数据库版本&#xff1a;KingbaseES V008R006C008B0014 简介 介绍2个KingbaseES用于备份还原的工具&#xff1a; sys_dump&#xff1a;逻辑备份sys_restore&#xff1a;逻辑还原 sys_dump 是 KingbaseES 用于逻辑备份的工具&#xff0c;可以将数据备份为不同类型的文件。支持数据…

ARM功耗管理标准接口之SCMI

安全之安全(security)博客目录导读 思考&#xff1a;功耗管理有哪些标准接口&#xff1f;ACPI&PSCI&SCMI&#xff1f; Advanced Configuration and Power Interface Power State Coordination Interface System Control and Management Interface 下图示例说明了实现…

docker部署canal 并监听mysql

1.部署mysql 需要开启mysql的binlong&#xff0c;和创建好用户等 可以参考这个 Docker部署Mysql数据库详解-CSDN博客 2.部署canal 参考这一篇&#xff1a; docker安装Canal&#xff0c;开启MySQL binlog &#xff0c;连接Java&#xff0c;监控MySQL变化_docker canal-CSD…

内网信息收集——MSF信息收集浏览器记录配置文件敏感信息

文章目录 一、配置文件敏感信息收集二、浏览器密码&记录三、MSF信息收集 域控&#xff1a;windows server 2008 域内机器&#xff1a;win7 攻击机&#xff1a;kali 就是红日靶场&#xff08;一&#xff09;的虚拟机。 一、配置文件敏感信息收集 使用searchall64.exe&#…

【错题集-编程题】四个选项(DFS + 剪枝 + 哈希表)

牛客对应题目链接&#xff1a;四个选项 (nowcoder.com) 一、分析题目 用递归枚举出所有的情况&#xff0c;注意剪枝&#xff1a; 填写某个数时&#xff0c;要看看还有没有剩余次数。填写某个数时&#xff0c;要看看符不符合若干题的选项必须相同。 二、代码 // 值得学习的代码…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(六)-人工智能控制的自主无人机用例

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

SparkStreaming--scala

文章目录 第1关&#xff1a;QueueStream代码 第2关&#xff1a;File Streams代码 第1关&#xff1a;QueueStream 任务描述 本关任务&#xff1a;编写一个清洗QueueStream数据的SparkStreaming程序。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.如何使用S…