数据结构——堆

数据结构——堆

    • 堆简介
    • 堆的分类
  • 二叉堆
    • 过程
      • 插入操作
    • 删除操作
      • 向下调整:
    • 增加某个点的权值
    • 实现
    • 参考代码:
    • 建堆
      • 方法一:使用 decreasekey(即,向上调整)
      • 方法二:使用向下调整
  • 应用
    • 对顶堆
  • 其他:
    • 配对堆:
    • 左偏树:

堆简介

堆是一棵树,其每个节点都有一个键值,且每个节点的键值都大于等于/小于等于其父亲的键值。

每个节点的键值都大于等于其父亲键值的堆叫做小根堆,否则叫做大根堆。STL 中的 priority_queue 其实就是一个大根堆。

(小根)堆主要支持的操作有:插入一个数、查询最小值、删除最小值、合并两个堆、减小一个元素的值。

一些功能强大的堆(可并堆)还能(高效地)支持 merge 等操作。

一些功能更强大的堆还支持可持久化,也就是对任意历史版本进行查询或者操作,产生新的版本。

堆的分类

在这里插入图片描述
习惯上,不加限定提到「堆」时往往都指二叉堆。

二叉堆

结构
从二叉堆的结构说起,它是一棵二叉树,并且是完全二叉树,每个结点中存有一个元素(或者说,有个权值)。

堆性质:父亲的权值不小于儿子的权值(大根堆)。同样的,我们可以定义小根堆。本文以大根堆为例。

由堆性质,树根存的是最大值(getmax 操作就解决了)。

过程

插入操作

插入操作是指向二叉堆中插入一个元素,要保证插入后也是一棵完全二叉树。

最简单的方法就是,最下一层最右边的叶子之后插入。

如果最下一层已满,就新增一层。

插入之后可能会不满足堆性质?

向上调整:如果这个结点的权值大于它父亲的权值,就交换,重复此过程直到不满足或者到根。

可以证明,插入之后向上调整后,没有其他结点会不满足堆性质。

向上调整的时间复杂度是 O ( l o g n ) O(log n) O(logn)的。

删除操作

删除操作指删除堆中最大的元素,即删除根结点。

但是如果直接删除,则变成了两个堆,难以处理。

所以不妨考虑插入操作的逆过程,设法将根结点移到最后一个结点,然后直接删掉。

然而实际上不好做,我们通常采用的方法是,把根结点和最后一个结点直接交换。

于是直接删掉(在最后一个结点处的)根结点,但是新的根结点可能不满足堆性质……

向下调整:

在该结点的儿子中,找一个最大的,与该结点交换,重复此过程直到底层。

可以证明,删除并向下调整后,没有其他结点不满足堆性质。

时间复杂度 O ( l o g n ) O(log n) O(logn)

增加某个点的权值

很显然,直接修改后,向上调整一次即可,时间复杂度为 O ( l o g n ) O(log n) O(logn)

实现

我们发现,上面介绍的几种操作主要依赖于两个核心:向上调整和向下调整。

考虑使用一个序列 h h h 来表示堆。 h i h_i hi 的两个儿子分别是 h 2 h_2 h2 i _i i h 2 h_2 h2 i _i i + _+ + 1 _1 1 1 1 1 是根结点:

在这里插入图片描述

参考代码:

void up(int x) {while (x > 1 && h[x] > h[x / 2]) {swap(h[x], h[x / 2]);x /= 2;}
}void down(int x) {while (x * 2 <= n) {t = x * 2;if (t + 1 <= n && h[t + 1] > h[t]) t++;if (h[t] <= h[x]) break;std::swap(h[x], h[t]);x = t;}
}

建堆

考虑这么一个问题,从一个空的堆开始,插入 n 个元素,不在乎顺序。

直接一个一个插入需要 O ( n l o g n ) O(n log n) O(nlogn) 的时间,有没有更好的方法?

方法一:使用 decreasekey(即,向上调整)

从根开始,按 BFS 序进行。

void build_heap_1() {for (i = 1; i <= n; i++) up(i);
}

为啥这么做:对于第 k 层的结点,向上调整的复杂度为 O ( k ) O(k) O(k) 而不是 O ( l o g n ) O(log n) O(logn)

总复杂度: l o g 1 log 1 log1 + l o g 2 log 2 log2 + … + l o g n log n logn = O ( n l o g n ) O(n log n) O(nlogn)

(在「基于比较的排序」中证明过)

方法二:使用向下调整

这时换一种思路,从叶子开始,逐个向下调整

void build_heap_2() {for (i = n; i >= 1; i--) down(i);
}

换一种理解方法,每次「合并」两个已经调整好的堆,这说明了正确性。

注意到向下调整的复杂度,为 O ( l o g n − k ) O(log n - k) O(lognk),另外注意到叶节点无需调整,因此可从序列约 n/2 的位置开始调整,可减少部分常数但不影响复杂度。

之所以能 O ( n ) O(n) O(n) 建堆,是因为堆性质很弱,二叉堆并不是唯一的。
要是像排序那样的强条件就难说了。

应用

对顶堆

这个问题可以被进一步抽象成:动态维护一个序列上第 k k k 大的数, k k k 值可能会发生变化。

对于此类问题,我们可以使用对顶堆这一技巧予以解决(可以避免写权值线段树或 B S T BST BST带来的繁琐)。

对顶堆由一个大根堆与一个小根堆组成,小根堆维护大值即前 k k k 大的值(包含第 k k k 个),大根堆维护小值即比第 k k k 大数小的其他数。

这两个堆构成的数据结构支持以下操作:

1.维护:当小根堆的大小小于 k k k 时,不断将大根堆堆顶元素取出并插入小根堆,直到小根堆的大小等于 k k k;当小根堆的大小大于 k k k 时,不断将小根堆堆顶元素取出并插入大根堆,直到小根堆的大小等于 k k k

2.插入元素:若插入的元素大于等于小根堆堆顶元素,则将其插入小根堆,否则将其插入大根堆,然后维护对顶堆;

3.查询第 k 大元素:小根堆堆顶元素即为所求;

4.删除第 k 大元素:删除小根堆堆顶元素,然后维护对顶堆;

显然,查询第 k k k 大元素的时间复杂度是 O ( 1 ) O(1) O(1) 的。由于插入、删除或调整 k k k 值后,小根堆的大小与期望的 k k k 值最多相差 1 1 1,故每次维护最多只需对大根堆与小根堆中的元素进行一次调整,因此,这些操作的时间复杂度都是 O ( l o g n ) O(log n) O(logn) 的。

其他:

配对堆:

详见:链接: 数据结构——配对堆

左偏树:

详见后文。

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

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

相关文章

Python Flask+Echarts+sklearn+MySQL(评论情感分析、用户推荐、BI报表)项目分享

Python FlaskEchartssklearnMySQL(评论情感分析、用户推荐、BI报表)项目分享 项目背景&#xff1a; 随着互联网的快速发展和智能手机的普及&#xff0c;人们越来越倾向于在网上查找餐厅、购物中心、酒店和旅游景点等商户的点评和评分信息&#xff0c;以便做出更好的消费决策。…

YOLOv8 : 网络结构

一. YOLOv8网络结构 1. Backbone YOLOv8的Backbone同样参考了CSPDarkNet-53网络&#xff0c;我们可以称之为CSPDarkNet结构吧&#xff0c;与YOLOv5不同的是&#xff0c;YOLOv8使用C2f(CSPLayer_2Conv)代替了C3模块(如果你比较熟悉YOLOv5的网络结构&#xff0c;那YOLOv8的网络…

【GitHub】Pycharm本地项目打包上传到Github仓库的操作步骤

文章目录 1、Pycharm端的设置操作2、Github端的设置操作3、Pycharm上配置Github4、Git本地项目至GitHub仓库5、前往Github中查看确认6、常见报错 1、Pycharm端的设置操作 通过CtrlAltS快捷组合键的方式&#xff0c;打开设置&#xff0c;导航到版本控制一栏中的Git&#xff0c;…

Gin安装解决国内go 与 热加载

get 方式安装超时问题&#xff0c;国内直接用官网推荐的下面这个命令大概率是安装不成功的 go get -u github.com/gin-gonic/gin 可以在你的项目目录下执行下面几个命令&#xff1a; 比如我的项目在E:\Oproject\zl cmd E:\Oproject\zl>就在目录下执行 go env -w GO111…

回归预测 | MATLAB实现GRU门控循环单元多输入多输出

回归预测 | MATLAB实现GRU门控循环单元多输入多输出 目录 回归预测 | MATLAB实现GRU门控循环单元多输入多输出预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 MATLAB实现GRU门控循环单元多输入多输出&#xff0c;数据为多输入多输出预测数据&#xff0c;输入10个…

pytorch安装VAE项目详解

安装VAE项目 一、 基本环境二、代码来源三、搭建conda环境四、下载数据集五、启动项目六、其他相关问题 一、 基本环境 工具版本号OSwin 11pycharm2020.1GPU3050 二、代码来源 github地址为&#xff1a; https://github.com/AntixK/PyTorch-VAE/blob/8700d245a9735640dda458d…

ZooKeeper的应用场景(集群管理、Master选举)

5 集群管理 随着分布式系统规模的日益扩大&#xff0c;集群中的机器规模也随之变大&#xff0c;因此&#xff0c;如何更好地进行集群管理也显得越来越重要了。 所谓集群管理&#xff0c;包括集群监控与集群控制两大块&#xff0c;前者侧重对集群运行时状态的收集&#xff0c;后…

08 - 追加commit和修改最新的commit message

查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;GIT常用场景- 目录 文章目录 1. 追加提交2. 修改最新的commit message 1. 追加提交 将改动追加到上一次的commit 现在我已经修改了Readme文件并且已经add、commit操作&#xff0c;但是还没有push到远程仓库&#x…

【左神算法刷题班】第17节:在有序二维数组中查找目标值、等于目标字符串的子序列个数

第17节 题目1&#xff1a;在有序二维数组中查找目标值 给定一个每一行有序、每一列也有序&#xff0c;整体可能无序的二维数组 再给定一个数num&#xff0c; 返回二维数组中有没有num这个数 例子 数组如下&#xff0c;找 6 是否存在。 1 3 5 7 2 4 6 13 3 9 14 …

“心理健康人工智能产学研创新联盟”揭牌成立|深兰科技

8月14日上午&#xff0c;“2023树洞救援年会”在上海举行&#xff0c;会上举行了“心理健康人工智能产学研创新联盟”的签约和揭牌仪式。“树洞行动救援团”创始人深兰科技科学院智能科学首席科学家、荷兰阿姆斯特丹自由大学人工智能系终身教授黄智生&#xff0c;深兰科技集团创…

Git 常用操作

一、Git 常用操作 1、Git 切换分支 git checkout命令可以用于三种不同的实体&#xff1a;文件&#xff0c;commit&#xff0c;以及分支。checkout的意思就是对于一种实体的不同版本之间进行切换的操作。checkout一个分支&#xff0c;会更新当前的工作空间中的文件&#xff0c;…

68 # 中间层如何请求其他服务

前端 ajax 有跨域问题&#xff0c;可以先访问中间层&#xff0c;在通过 node 去请求别的服务端口&#xff0c;可以解决跨域问题 编写中间层调用 // 中间层的方式const http require("http");// http.get 默认发送 get 请求 // http.request 支持其他请求格式 postl…

Java基础知识实际应用(学生信息管理系统、猜拳小游戏、打印日历)

一、Java学生信息管理系统 这个系统包含了添加、修改、删除、查询和显示所有学生信息等功能。您可以在此基础上进行修改和完善&#xff0c;以适应您的需求。 import java.util.Scanner;public class StudentManagementSystem {private static Scanner scanner new Scanner(S…

haproxy负载均衡

1、配置环境 作用环境windows测试  192.168.33.158 172.25.0.11 haproxy负载均衡haproxy&#xff1a;2.8.1&#xff0c;centos7172.25.0.31web服务器1--rs1Apache&#xff1a;2.4&#xff0c;redhat9172.25.0.32web服务器2--rs2Apache&#xff1a;2.4 &#xff0c; redhat9 2、…

Azure使用CLI创建VM

使用CLI创建VM之前&#xff0c;确保资源中的IP资源已经释放掉了&#xff0c;避免创建的过程中没有可以利用的公共IP地址打开 cloudshell ,并输入创建CLI的命令如下&#xff0c;-n指定名称&#xff0c;-g指定资源组&#xff0c;image指定镜像&#xff0c;admin-usernam指定用户名…

Maven进阶1 -- 分模块开发、依赖管理、聚合与继承、属性、版本管理、多环境开发、跳过测试

目录 1.分模块开发 将原始模块按照功能拆分成若干个子模块&#xff0c;方便模块间的相互调用&#xff0c;接口共享。 案例&#xff1a;拆分一下这个SSM整合案例 ①创建maven模块 demo项目下的pom.xml文件&#xff08;主要看一下依赖&#xff09; <dependencies><!…

【wiki】电竞助手掉落提醒 EsportsHelper「Webhook」「钉钉」「饭碗警告」「企业微信」「Discord」

介绍 本项目链接 Github电竞助手链接 github上项目电竞助手(EsportsHelper)的掉落提醒配置教程,当有掉宝的时候会发送你信息提示. 至于这个脚本是怎么使用的简单说一下,就是通过自动观看英雄联盟直播 从而获取奖励(仅限直营服),有兴趣的可以去github上看readme,非常详细,支持…

javaScript:数组检测

目录 一.前言 二.数组检测方法 1.every&#xff08;&#xff09; 2.some&#xff08;&#xff09; 3.filter&#xff08;&#xff09; 一.前言 数组检测是指在编程中对数组进行验证和检查的过程。数组检测可以涉及以下方面&#xff1a; 确定数组的存在&#xff1a;在使用数…

一种多策略下RabbitMQ的延时队列实现

1.为什么会用到延时队列? 场景: 最近在开发一款系统中遇到这样一个场景,A系统开通套餐需要把套餐信息以邮件的形式发送给相关工作人员,经过人工审核通过后,在B系统里面开通,A系统会调B系统套餐列表接口查询套餐是否开通成功,开通成功则从A系统去完成订单,假如超过设定时间未开…

韦东山-电子量产工具项目:显示单元

所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 disp_manager.h #ifndef _DISP_MANAGER_H //防止头文件重复包含,只要右边的出现过&#xff0c;就不会再往下编译 #define _DISP_MANAGER_H //区域结构体 typedef struct DispBuff …