【第十五课】数据结构:堆 (“堆”的介绍+主要操作 / acwing-838堆排序 / c++代码 )

目录

关于堆的一些知识的回顾 

数据结构:堆的特点

"down" 和 "up":维护堆的性质

down

up

数据结构:堆的主要操作

acwing-838堆排序

代码如下

时间复杂度分析


确实是在写的过程中频繁回顾了很多关于树的知识,有关的文章都在专栏里,需要的可以去回顾一下~

http://t.csdnimg.cn/0d6Iqicon-default.png?t=N7T8http://t.csdnimg.cn/0d6Iq

关于堆的一些知识的回顾 

关于堆,我的印象中是内存机制里的堆。之前写过的,在回顾一下吧~

然而我们这里说的堆,其实是一种数据结构中的完全二叉树实现的堆

(我们这里图片上写的坐标索引方式是根节点从0开始索引,我们下面会采用下标从1开始索引,那样的话,左儿子就应该是2x 右儿子是2x+1 可以理解哈)

其实这里还要回顾一下树的广度优先遍历,即一层一层,从左到右的遍历方式,对于完全二叉树来说,其广度优先遍历就是创建一个数组,按照特定的存储方式进行存储,最终直接输出数组元素。对于其他的树,采取队列的存储方式。下面这篇文章详细介绍了树的遍历,也对数组存储有更深的解释,感兴趣可以看一下

http://t.csdnimg.cn/NngZ6


数据结构:堆的特点

1.完全二叉树的结构 

堆是一个完全二叉树,这意味着除了最底层,其他层都是满的,而最底层的节点都集中在左侧。

到这里就要疑惑为什么堆是完全二叉树这种结构了 

http://www.zhihu.com/question/36134980/answer/87490177

这篇文章作者详细解释了关于"堆"这种数据结构好处包括用途,我感觉写的非常不错,可以看一下加深理解。

其中主要提到:1.完全二叉树这种结构可以使用数组实现存储,并且便于索引

2.它的出现是为了解决----对一个动态的序列进行排序,并且随时想知道这个序列的最小值或最大值

2.最小堆和最大堆

堆可以分为最小堆和最大堆两种类型。在最小堆中,每个节点的值都小于或等于其子节点的值。而在最大堆中,每个节点的值都大于或等于其子节点的值。(也叫小根堆和大根堆)

"down" 和 "up":维护堆的性质

"down" 操作通常涉及到将元素向下移动,适用于删除操作和堆化过程中。

通过 down(k) 进行下沉操作是为了调整以 k 为根的子树,确保其满足最小堆的性质。这主要关注了 k 节点向下的关系

"up" 操作涉及到将元素向上移动,适用于插入操作和堆化过程中。 

通过 up(k) 进行上浮操作是为了确保从删除元素的位置 k 开始,向上到根节点的路径上的每个父节点都满足最小堆的性质。这主要关注了 k 节点向上的关系

而我们所说的 "down" 和 "up" 是通常用于--维护堆的性质。以确保堆的性质不被破坏。

下面以小根堆为例 

down

根据这个思路我们写出代码

//调整以x为根的子树,以满足小根堆的性质(x是经过某种操作得到的值,在操作之前整棵树是满足堆的性质的)
void down(int x)
{
    int t=x;//t表示三个数中的最小值
//比较的前提都是孩子存在
    if(x*2<=size && he[x*2]<he[t])t=x*2;

    if(x*2+1<=size && he[x*2+1]<he[t])t=x*2+1;
    if(x!=t)//所以这里如果不需要交换位置,那说明更改的这个值并没有破坏原有的性质
    {
        swap(he[x],he[t]);
        down(t);//需要交换 说明我们又更改了一个位置的值,所以要继续判断这次更改的是否符合性质
    }
}

down操作的前提就是 本身这个树的每个节点都是符合性质的,只是某一个值发生了改变

我们针对这个发生改变的值,不管它是插入还是修改还是更改而导致的值的变化,我们最主要的就是关注改值之后,以其为根节点的子树。

将该值与它的原本的左右两个孩子的值的比较,如果不需要交换位置,说明这次的更改的值并没有引起堆的性质的变化

up

如上图这种完全二叉树,按照数组存储方式,观察其下标表示,我们发现孩子节点是其父节点下标的二倍

由于小根堆的性质,根节点小于左右孩子,所以我们检查的时候只看该节点与根节点的大小关系就好了,因为另一个孩子一定是大于根节点且符合性质的

void up(int x)
{
    while(x/2 && he[x/2]>he[x])//当该元素存在父节点且满足大小关系
    {
        swap(he[x/2],he[x]);
        x /= 2;//更新父节点
    }
}

有了上面down的详细解释这个应该很容易理解了。 

数据结构:堆的主要操作

1.插入一个元素,并仍保持堆的性质

2.删除最小/大值。

3.堆化:将一个无序数组转换为堆,或者修复一个破坏了堆性质的堆。

这里先详细说一下堆化

for(int i=n/2;i;i--)down(i);

我们通常从倒数第二层(n/2是最后一个元素的父节点)开始进行逐个元素下沉,最终达到将数组堆化的结果。 这是因为底层节点是叶子节点,它们自身已经满足堆的性质,不需要进行下沉操作。


关于手写堆的一些主要操作就是上面这些。

下面我逐个来解释。

1.插入一个数。由于我们堆结构是用数组实现存储的完全二叉树,因此对于数组来说,在末尾添加一个元素是很容易的,所以我们先把这个要插入的元素放到堆的末尾,在进行up操作,使其符合堆的性质。

2.求最小值,我们小根堆的根节点就是其最小值。

3.删除最小值。由于我们总是要删除一个元素的,在数组存储结构中,删除最后一个元素是很简单的,直接使我们使用到的下标--就行,但是我们要删的是第一个元素呀,怎么办呢?我们把最后一个元素的值标记覆盖到第一个元素的位置,再删除掉最后一个元素,再把刚刚放到第一位的元素进行down操作,使其符合堆的性质。

4.删除任意一个元素。在3思路的基础上,由于我们不清楚最后一个元素相较于原来这个位置上删除掉的元素是大还是小,如果是大于原来的元素,那就会执行down,如果小于,就会向上执行up,因此,这两个只会执行其中一个,为了简化代码,我们直接不判断,把两个都放上去

5.修改任意一个元素,解释同4

acwing-838堆排序

经过上面的介绍其实这道题的核心都已经解了,直接看代码把。

代码如下

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int he[N],size;

//调整以 x 为根的子树,以满足小根堆的性质
void down(int x)
{
    int t=x;//t表示三个数中的最小值
    if(x*2<=size && he[x*2]<he[t])t=x*2;
    if(x*2+1<=size && he[x*2+1]<he[t])t=x*2+1;
    if(x!=t)
    {
        swap(he[x],he[t]);
        down(t);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&he[i]);
    }

    size=n;//size标记数组最后一个元素

    for(int i=n/2;i;i--)down(i);//将数组堆化
    
    while(m--)//每次输出一个最小值
    {
        printf("%d ",he[1]);
        he[1]=he[size];//删除最小值
        size--;
        down(1);
    }
    return 0;
}

时间复杂度分析

在这段代码中:

输入数据的for循环O(n) 

建堆的for循环,先看down函数,down是逐层比较,其时间复杂度取决于树的高度,所以最坏情况下就是该二叉树为满二叉树时的树高,即log(n)。那么真整个建堆过程时间复杂度应为O(n*log(n)) 

--关于这里的时间复杂度其实还有一种思考方式,等之后回来补写一下。

输出最小值的while循环,也是取决于down函数执行次数,即O(m*log(n)) 


哎,就差一个模拟堆(烦躁),是有点强迫症在的😢明天在写啦。。

有问题欢迎指出!一起加油!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为发布 HarmonyOS NEXT 鸿蒙星河版

文章目录 个人简介 在 2024-01-18 下午于深圳举办的鸿蒙生态千帆启航仪式上&#xff0c;华为常务董事兼终端BG CEO余承东宣布了HarmonyOS NEXT&#xff08;鸿蒙星河版&#xff09;的开发者预览版面向开发者开放申请。这一版本旨在实现六大极致原生体验&#xff0c;包括原生精致…

【某某大学的探索之旅】奇怪的登录框概率性布尔报错盲注绕过

在某某大学的探索过程中&#xff0c;发现了一个比较奇怪的布尔报错盲注 它这里本来登录有一个滑动验证码&#xff0c;token是滑动验证码每次校验生成的&#xff0c;从处理逻辑讲&#xff0c;这里的token是不能复用的&#xff0c;但是这里的token却是可以复用&#xff0c;这本来…

【分布式技术】消息队列Kafka

目录 一、Kafka概述 二、消息队列Kafka的好处 三、消息队列Kafka的两种模式 四、Kafka 1、Kafka 定义 2、Kafka 简介 3、Kafka 的特性 五、Kafka的系统架构 六、实操部署Kafka集群 步骤一&#xff1a;在每一个zookeeper节点上完成kafka部署 ​编辑 步骤二&#xff1a…

喜讯 | 华院计算摘得“2023大数据产业年度创新技术突破”奖

2024年1月17日&#xff0c; 由数据猿和上海大数据联盟主办&#xff0c;上海市经济和信息化委员会、上海市科学技术委员会指导的“第六届金猿季&魔方论坛——大数据产业发展论坛”在上海市四行仓库举行。论坛以“小趋势大未来”为主题&#xff0c;围绕大数据产业的各个领域展…

〖大前端 - ES6篇①〗- ES6简介

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…

【表情识别阅读笔记】Towards Semi-Supervised Deep FER with An Adaptive Confidence Margin

论文名&#xff1a; Towards Semi-Supervised Deep Facial Expression Recognition with An Adaptive Confidence Margin 论文来源&#xff1a; CVPR 发表时间&#xff1a; 2022-04 研究背景&#xff1a; 对大量图片或视频进行手工标注表情是一件极其繁琐的事情&#xff0c;因此…

UDP和TCP代理协议有什么区别?哪个更好

在互联网的世界里&#xff0c;数据传输的方式有很多种&#xff0c;其中 UDP 和 TCP 是两种常见的传输协议。而代理协议则是为了在网络中传输数据时提供安全、稳定和高效的传输环境。那么&#xff0c;UDP 和 TCP 代理协议有什么区别呢&#xff1f;哪个更好呢&#xff1f;接下来&…

C++版QT:电子时钟

digiclock.h #ifndef DIGICLOCK_H #define DIGICLOCK_H ​ #include <QLCDNumber> ​ class DigiClock : public QLCDNumber {Q_OBJECT public:DigiClock(QWidget* parent 0);void mousePressEvent(QMouseEvent*);void mouseMoveEvent(QMouseEvent*); public slots:voi…

docker - compose 部署 Tomcat

目录 下面用 docker-compose 方法部署 Tomcat 1、准备工作 2、部署容器 启动容器 查看新启动的容器 3、总结 下面用 docker-compose 方法部署 Tomcat 1、准备工作 先在主机创建工作文件夹&#xff0c;为了放置 Tomcat 的配置文件等。创建文件夹的方法&#xff0c;自己搞…

Kubernetes operator(一)client-go篇【更新中】

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Kubernetes operator学习 系列第一篇&#xff0c;主要对client-go进行学习&#xff0c;从源码阅读角度&#xff0c;学习client-go各个组件的实现原理、如何协同工作等参考视频&#xff1a;Bilibili 2022年最新k…

应用app的服务器如何增加高并发

增强服务器的高并发能力是现代网络应用非常关键的需求。面对用户数量的不断增长和数据量的膨胀&#xff0c;服务器必须能够处理大量并发请求。以下是一些提高服务器高并发能力的常用方法和具体实施细节&#xff1a; 优化服务器和操作系统配置 服务器和操作系统的默认配置不一定…

大模型学习与实践笔记(十二)

将RAG生成模型部署到openxlab 平台 代码仓库&#xff1a;https://github.com/AllYoung/LLM4opencv 1&#xff1a;创建代码仓库 在 GitHub 中创建存放应用代码的仓库&#xff0c;其代码大致目录树如下&#xff1a; ├─GitHub repo │ ├─app.py # …

多场景建模:阿里多场景多任务元学习方法M2M

multi-scenario multi-task meta learning approach (M2M) 背景 广告领域大部分是针对用户建模的&#xff0c;像点击率预估&#xff0c;很少有针对广告主需求建模&#xff08;广告消耗预估、活跃率/流失率预估、广告曝光量预估&#xff09;&#xff0c;广告的类型较多&#x…

数据库-分库分表初探

文章目录 分库策略垂直切分垂直分库&#xff08;专库专用&#xff09;垂直分表&#xff08;拆表&#xff09;优点缺点 水平(Sharding)切分水平分表库内分表分库分表优点缺点 分表策略hash取模方案range范围区间取值方案映射表方案 分库分表问题事务一致性问题跨节点关联查询跨节…

隐藏服务器源IP的几种方法

为网络管理员的我们多知道遇到过服务器因为拒绝服务攻击(DDOS攻击)遇到网站瘫痪的情况是很糟心&#xff0c;随着客户信息越来越受到公司企业的重视&#xff0c;网站服务器的安全也越来越受到关注&#xff0c;但无法避免的是会遇到黑客使用DDoS攻击业务。 下面简单介绍一下隐藏i…

PolarDB无感切换特性助力游戏领域高可用实践

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

前端使用css去除input框的默认样式

关键点&#xff1a; /* 关键点&#xff0c;让输入框无边框 */outline:none; border:none; 1.效果图 2.html <div class"container"><input type"text" placeholder"请输入用户名"><input type"text" placeholder&q…

如何在WordPress中使用 AI 进行 SEO(12 个工具)

您想在 WordPress 中使用 AI 进行 SEO 吗&#xff1f; 人工智能正在对 SEO 行业产生重大影响。已经有优秀的人工智能 SEO 工具&#xff0c;您可以使用它们来提高您的 SEO 排名&#xff0c;而无需付出太多努力。 在本文中&#xff0c;我们将向您展示如何通过我们精心挑选的工具…

深入解析互联网医院APP开发流程与源码搭建

本篇文章&#xff0c;深入解析互联网医院APP的开发流程&#xff0c;并提供关于源码搭建的一些建议。 一、确定需求与功能 在开始互联网医院APP的开发之前&#xff0c;首先需要明确项目的需求和功能。这包括用户端的预约挂号、在线咨询、报告查看等功能&#xff0c;以及医生端…

spawn_group_template | spawn_group | linked_respawn

字段介绍 spawn_group | spawn_group_template 用来记录与脚本事件或boss战斗有关的 creatures | gameobjects 的刷新数据linked_respawn 用来将 creatures | gameobjects 和 boss 联系起来&#xff0c;这样如果你杀死boss&#xff0c; creatures | gameobjects 在副本重置之前…