数据结构--堆

一. 堆

1. 堆的概念

堆(heap):一种有特殊用途的数据结构——用来在一组变化频繁(发生增删查改的频率较高)的数据集中查找最值。
堆在物理层面上,表现为一组连续的数组区间:long[] array ;将整个数组看作是堆。
堆在逻辑结构上,一般被视为是一颗完全二叉树。
满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆;反之,则是小堆,或者小根堆,或者最小堆。当一个堆为大堆时,它的每一棵子树都是大堆。
在这里插入图片描述

2. 堆的存储方式

从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储;
假设 i 为结点在数组中的下标,则有:
在这里插入图片描述
如果 i 为 0,则 i 表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2;
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子;
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子。

二. 堆的基本操作

1. 创建堆,向下调整与向上调整

创建堆只有两种堆可以创建,要不就是大根堆,要不就是小根堆。而要满足大根堆还是小根堆的逻辑,就要向下调整的操作才能实现。要想自己实现堆,堆本身就是一个数组,因此创建一个数组来创建堆。
对于集合 { 27,15,19,18,28,34,65,49,25,37 } 中的数据,如果将其创建成堆呢?
在这里插入图片描述
仔细观察上图后发现:根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可。 向下过程(以小堆为例):

  1. 让 parent 标记需要调整的节点,child 标记 parent 的左孩子(注意:parent 如果有孩子一定先是有左 孩子)
  2. 如果 parent 的左孩子存在,即: child < size, 进行以下操作,直到 parent 的左孩子不存在:
    • 看 parent 右孩子是否存在,存在找到左右孩子中最小的孩子,让 child 进行标
    • 将 parent 与较小的孩子 child 比较,如果:
    • parent 小于较小的孩子 child,调整结束;
    • 否则:交换 parent 与较小的孩子 child,交换完成之后,parent 中大的元素向下移动,可能导致子树不满足对的性质,因此需要 继续向下调整,即 parent = child;child = parent*2+1;然后继续 2

在这里插入图片描述

def sift(li, low, high):"""建立大根堆:param li: 列表:param low: 堆的根节点位置:param high: 堆的最后一个元素的位置:return:"""i = low  # 最开始指向根节点j = 2 * i + 1  # 开始是左孩子tmp = li[low]  # 把堆顶存起来# 只要j位置有数while j <= high:# 左孩子和右孩子比较大小 右孩子有没有越界 且 右孩子比左孩子大if j + 1 <= high and li[j + 1] > li[j]:j = j + 1  # 把j指向右孩子# 比较堆顶的tmp和j左右孩子大小比较if li[j] > tmp:  # 如果孩子比堆顶大li[i] = li[j]  # 把孩子大的换到上面父节点# 往下看一层,将i移动到孩子位置,将j继续向下移动到新i的孩子的位置i = jj = 2 * i + 1else:  # tmp更大,把tmp放到i的位置上 结束循环li[i] = tmp  # 把tmp放到某一级领导位置上break# 越界了else:li[i] = tmp  # 说明i走到最下面一层了,j到还要下一层没有叶子节点位置,是空的,就把tmp放到叶子节点上
def sift(li, low, high):"""建立小根堆:param li: 列表:param low: 堆的根节点位置:param high: 堆的最后一个元素的位置:return:"""i = low  # 最开始指向根节点j = 2 * i + 1  # 开始是左孩子tmp = li[low]  # 把堆顶存起来# 只要j位置有数while j <= high:# 左孩子和右孩子比较大小 右孩子有没有越界 且 右孩子比左孩子小if j + 1 <= high and li[j + 1] < li[j]:j = j + 1  # 把j指向右孩子# 比较堆顶的tmp和j左右孩子大小比较if li[j] < tmp:  # 如果孩子比堆顶小li[i] = li[j]  # 把孩子大的换到上面父节点# 往下看一层,将i移动到孩子位置,将j继续向下移动到新i的孩子的位置i = jj = 2 * i + 1else:  # tmp更大,把tmp放到i的位置上 结束循环li[i] = tmp  # 把tmp放到某一级领导位置上break# 越界了else:li[i] = tmp  # 说明i走到最下面一层了,j到还要下一层没有叶子节点位置,是空的,就把tmp放到叶子节点上

建堆的时间复杂度是 O(n) ;向下调整的时间复杂度是 O(log(n))。

2. 堆的插入(offer)

堆的插入总共需要两个步骤:

  1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
  2. 将最后新插入的节点向上调整,直到满足堆的性质 ;

在这里插入图片描述

3. 堆的删除(poll)

具体如下:( 注意:堆的删除一定删除的是堆顶元素。

  1. 将堆顶元素对堆中最后一个元素交换;
  2. 将堆中有效数据个数减少一个;
  3. 对堆顶元素进行向下调整;
    代码待补充…

三. 堆的应用

1. 堆排序(从小到大排)

一个数组根据从小到大排序,要创建大堆来排;一个数组根据从大到小排序,要创建小堆来排。
此处就以创建大堆为例。首先将堆顶的元素和堆中的最后一个元素交换,交换后再向下调整,调整后再与堆的倒数第二个元素进行交换。

def sift(li, low, high):"""向下调整的一次过程:param li: 列表:param low: 堆的根节点位置:param high: 堆的最后一个元素的位置:return:"""i = low  # 最开始指向根节点j = 2 * i + 1  # 开始是左孩子tmp = li[low]  # 把堆顶存起来# 只要j位置有数while j <= high:# 左孩子和右孩子比较大小 右孩子有没有越界 且 右孩子比左孩子大if j + 1 <= high and li[j + 1] > li[j]:j = j + 1  # 把j指向右孩子# 比较堆顶的tmp和j左右孩子大小比较if li[j] > tmp:  # 如果孩子比堆顶大li[i] = li[j]  # 把孩子大的换到上面父节点# 往下看一层,将i移动到孩子位置,将j继续向下移动到新i的孩子的位置i = jj = 2 * i + 1else:  # tmp更大,把tmp放到i的位置上 结束循环li[i] = tmp  # 把tmp放到某一级领导位置上break# 越界了else:li[i] = tmp  # 说明i走到最下面一层了,j到还要下一层没有叶子节点位置,是空的,就把tmp放到叶子节点上# 堆排序过程
def heap_sort(li):"""1. 先建堆  从最后一个子堆开始,小堆到大堆 依次到根节点2. 向下调整 得到堆顶元素,为最大元素3. 挨个出数 堆顶最大元素和堆最后一个元素交换位置4. 重复2-3,直到堆变空:param li:待排序的列表:return:"""print("开始建大根堆")# n 列表长度n = len(li)# 遍历范围 首先求列表最后一个父元素,最后一个小堆,最后一个子元素下标是n - 1,父下标((n-1)-1))//2,通过左右孩子公式都一样的结果# 最后一个父元素开始,最后-1步长是倒着遍历到列表最后一个元素 找到堆顶0(中间-1,步长负数,-1+1=0),倒序遍历for i in range((n - 2) // 2, -1, -1):sift(li, i, n - 1)# for循环结束,建堆完成了# 挨个出数for i in range(n - 1, -1, -1):  # 倒序 i从最后开始# i指向当前堆的最后一个元素li[0], li[i] = li[i], li[0]# 由于是倒序,挨个出数后,尾部有序区指针high,每次左移一位sift(li, 0, i - 1)  # i-1是新的highli = [9, 6, 3, 5, 7, 2, 1, 8, 4]print(li)
heap_sort(li)
print(li)
2. top-k问题

若要从N个数字中取得最小的K个数字,则需要创建大小为K的大堆来获取。若要从N个数字中取得最大的K个数字,则需要创建大小为K的小堆来获取。

def sift(li, low, high):"""向上调整的一次过程:param li: 列表:param low: 堆的根节点位置:param high: 堆的最后一个元素的位置:return:"""i = low  # 最开始指向根节点j = 2 * i + 1  # 开始是左孩子tmp = li[low]  # 把堆顶存起来# 只要j位置有数while j <= high:# 左孩子和右孩子比较大小 右孩子有没有越界 且 右孩子比左孩子小if j + 1 <= high and li[j + 1] < li[j]:j = j + 1  # 把j指向右孩子# 比较堆顶的tmp和j左右孩子大小比较if li[j] < tmp:  # 如果孩子比堆顶小li[i] = li[j]  # 把孩子大的换到上面父节点# 往下看一层,将i移动到孩子位置,将j继续向下移动到新i的孩子的位置i = jj = 2 * i + 1else:  # tmp更大,把tmp放到i的位置上 结束循环li[i] = tmp  # 把tmp放到某一级领导位置上break# 越界了else:li[i] = tmp  # 说明i走到最下面一层了,j到还要下一层没有叶子节点位置,是空的,就把tmp放到叶子节点上def topk(li, k):# 先取列表前k个元素heap = li[0:k]# 1. 建小根堆for i in range((k - 2) // 2, -1, -1):sift(heap, i, k - 1)print("*" * 80)print("小根堆heap建堆完成,", heap)print("*" * 80)# 2. 遍历 li列表里k后面剩下的元素for i in range(k, len(li)):# 依次拿k后面的值和小根堆 堆顶的值比较大小if li[i] > heap[0]:  # 如果值 大于 堆顶元素值heap[0] = li[i]  # 把大的值 放到堆顶sift(heap, 0, k - 1)# 3. 挨个出数for i in range(k - 1, -1, -1):  # 倒序 i从最后开始# i指向当前堆的最后一个元素heap[0], heap[i] = heap[i], heap[0]sift(heap, 0, i - 1)  # i-1是新的highreturn heapli = [i for i in range(20)]
random.shuffle(li)print(li)
print(topk(li, 10))
print(li)

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

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

相关文章

MySQl_2

目录 函数 一.字符串函数 二.数值函数 三.日期函数 四.流程控制函数 约束 多表查询 多表关系 一.内连接 二.外连接 三.自连接 四.联合查询 五.子查询 标量子查询 列子查询 行子查询 表子查询 函数 一.字符串函数 二.数值函数 SELECT LPAD(FLOOR(RAND()*1000000),…

二叉树与递归的相爱相杀

数据结构之二叉树 一、基于二叉树的基础操作1.二叉树的构建2.二叉树的遍历①前序遍历&#xff08;深度遍历&#xff09;②中序遍历③后序遍历④层序遍历判断一棵二叉树是否是完全二叉树&#xff08;基于层序遍历的思想&#xff09; 3.二叉树的数量问题①求二叉树结点个数②求二…

PixMIM论文笔记

论文名称&#xff1a;PixMIM: Rethinking Pixel Reconstruction in Masked Image Modeling 发表时间&#xff1a;2023 年 3 月 4 日 作者及组织&#xff1a;上海人工智能实验室、西蒙菲莎大学、香港中文大学 GitHub&#xff1a;https://github.com/open-mmlab/mmselfsup/tree/d…

transformer_01

一、传统RNN存在的问题 1.序列前序太长&#xff0c;每个xi要记住前面的特征&#xff0c;而且一直在学&#xff0c;没有忘记&#xff0c;可能特征不能学的太好 2.串行&#xff0c;层越多越慢&#xff0c;难以堆叠很多层&#xff1b; 3.只能看到过去&#xff0c;不能看到未来 搞…

什么是NetApp的DQP和如何安装DQP?

首先看看什么是DQP&#xff0c;DQPDisk Qualification Package&#xff0c;文字翻译就是磁盘验证包。按照NetApp的最佳实践&#xff0c;要定期升级DQP包&#xff0c;保证对最新磁盘和磁盘扩展柜的兼容。 本文主要介绍7-mode下如何升级DQP&#xff0c;至于cluster mode另外文章…

gazebo各种插件

类别 libgazebo_ros_api_plugin.so&#xff1a;提供与Gazebo仿真环境进行通信的API接口。 libgazebo_ros_block_laser.so&#xff1a;模拟激光传感器的插件。 libgazebo_ros_bumper.so&#xff1a;模拟碰撞传感器的插件。 libgazebo_ros_camera.so&#xff1a;模拟相机传感器的…

Linux Zabbix企业级监控平台+cpolar实现远程访问

文章目录 前言1. Linux 局域网访问Zabbix2. Linux 安装cpolar3. 配置Zabbix公网访问地址4. 公网远程访问Zabbix5. 固定Zabbix公网地址 前言 Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。能监视各种网络参数&#xff0c;保证服务器系…

@RequestParam和@RequestBody部分使用场景总结

总结代码如下 package com.woer.receipt_callback.controller;import cn.hutool.log.StaticLog; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.web.bind.annotation.*;/*** 总结&#xff1a;* 一、Ge…

基于边缘网关构建水污染监测治理方案

绿水青山就是金山银山&#xff0c;生态环境才是人类最宝贵的财富。但是在日常生活生产中&#xff0c;总是免不了各种污水的生产、排放。针对生产生活与环境保护的均衡&#xff0c;可以借助边缘网关打造环境污水监测治理体系&#xff0c;保障生活与环境的可持续性均衡发展。 水污…

NewStarCTF2023week2-Upload again!

尝试传修改后缀的普通一句话木马&#xff0c;被检测 尝试传配置文件 .htaccess 和 .user.ini 两个都传成功了 接下来继续传入经过修改的木马 GIF89a <script language"php"> eval($_POST[cmd]); </script> 没有被检测&#xff0c;成功绕过 直接上蚁剑…

JavaScript的forEach循环和作用域

forEach循环 var age [12,3,12,3,12,12,1,3,3,123] age.forEach(function(value){console.log(value) }) for(var num in age){ if(age.hasOfProperty(num)){ console.log("存在") console.log(age[num]) } } num是下标位置&#xff0c; 通过get方法获取字符串相…

【算法与数据结构】--常见数据结构--树与图

一、二叉树 二叉树&#xff08;Binary Tree&#xff09;是一种重要的树状数据结构&#xff0c;它由节点构成&#xff0c;每个节点最多有两个子节点&#xff1a;一个左子节点和一个右子节点。这种结构使得二叉树在计算机科学和编程中具有广泛的应用。 1.1 二叉树的基本特性&am…

微查系统,一站式查询,让您的查询更加便捷

微查系统是挖数据一款功能强大的查询系统&#xff0c;是一个集多种查询和核验工具于一身的综合性平台。它可以大大简化企业和个人的查询流程&#xff0c;节省时间和成本&#xff0c;提高查询的准确性和效率。本文将介绍微查系统的主要特点&#xff0c;功能和使用方法&#xff0…

Springboot 集成 WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接…

python 批量使图片重新排序

** 导入相关库 ** import os设置图片文件夹的路径 image_folder " D:\\images"获取文件夹中的所有图片文件 image_files [f for f in os.listdir(image_folder) if f.endswith(".jpg")]确保文件夹中至少有一个图片文件 if not image_files:print(&qu…

【SA8295P 源码分析 (一)】52 - 答疑之 QNX 创建镜像、Android修改CMDLINE

【SA8295P 源码分析】52 - 答疑之 QNX 创建镜像、Android修改CMDLINE 一、QNX 侧创建 img 镜像二、QNX 侧指定只编译某一个版本三、Android定制修改selinux权限,user版本采用enforcing,userdebug版本permissive系列文章汇总见:《【SA8295P 源码分析 (一)】系统部分 文章链接…

C++数据结构X篇_15_求二叉树叶子数与高度(递归方法)

本篇参考求二叉树叶子数与高度&#xff08;C&#xff09;进行整理。 文章目录 1. 二叉树中叶子数与高度2. 求二叉树叶子数与高度的实现代码 1. 二叉树中叶子数与高度 我们首先来看一看二叉树中叶子数与高度的定义&#xff1a; 叶子数&#xff1a;对于一个二叉树的节点&#x…

新型的终端复用器 tmux

以前遇到长时间执行任务时&#xff0c;一般是使用nohup加后台运行&#xff0c;但是涉及到少量代码编写。 同事介绍了一个screen命令&#xff0c;根据文档&#xff0c;此命令已经过时&#xff0c;最新的命令是tmux。 tmux的介绍文档&#xff0c;RedHat的这一篇非常不错。 在文…

图详解第四篇:单源最短路径--Dijkstra算法

文章目录 1. 最短路径问题2. 单源最短路径--Dijkstra算法算法思想图解如何存储路径及其权值代码实现调式观察打印最短路径Dijkstra算法的缺陷 3. 源码 1. 最短路径问题 最短路径问题&#xff1a; 从带权有向图&#xff08;求最短路径通常是有向图&#xff09;G中的某一顶点出发…

为什么大势浏览器打开文件点进去都是一些分解的模型,没有一整块的模型?

答&#xff1a;选择Data文件夹&#xff0c;然后整体拖入Dasviewer&#xff0c;就可以整体查看了 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景三维模型,提供方便快捷的…