当前位置: 首页 > news >正文

数据结构--堆

一. 堆

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)

相关文章:

数据结构--堆

一. 堆 1. 堆的概念 堆&#xff08;heap&#xff09;&#xff1a;一种有特殊用途的数据结构——用来在一组变化频繁&#xff08;发生增删查改的频率较高&#xff09;的数据集中查找最值。 堆在物理层面上&#xff0c;表现为一组连续的数组区间&#xff1a;long[] array &…...

Android12之报错 error: BUILD_COPY_HEADERS is obsolete(一百六十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

vue前端中v-model与ref的区别

v-model <template><input type"text" v-model"message"> </template>作用&#xff1a;将输入框与message绑定&#xff0c;及将用户输入的内容绑定到message这个变量上&#xff0c;但是message是无法在script中获取到的&#xff0c;要想…...

探索未来:硬件架构之路

文章目录 &#x1f31f; 硬件架构&#x1f34a; 基本概念&#x1f34a; 设计原则&#x1f34a; 应用场景&#x1f34a; 结论 &#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…...

Linux 系统安装 Redis7 —— 超详细操作演示!

内存数据库 Redis7 一、Redis 概述1.1 Redis 简介1.2 Redis 的用途1.3 Redis 特性1.4 Redis 的IO模型 二、Redis 的安装与配置2.1 Redis 的安装2.2 连接前的配置2.3 Redis 客户端分类2.4 Redis 配置文件详解 三、Redis 命令四、Redis 持久化五、Redis 主从集群六、Redis 分布式…...

首次建站用香港服务器有影响没?

​  对于首次租用香港服务器的朋友来说&#xff0c;难免会对它没有一个很清晰的认知。因此&#xff0c;本文就从香港服务器适用人群&#xff0c;以及建站影响&#xff0c;选择技巧上做一个全方位的解答。 1. 哪一类人群适合使用香港服务器建站? 做外贸业务的网站。香港走的国…...

大数据Flink(九十八):SQL函数的归类和引用方式

文章目录 SQL函数的归类和引用方式 一、SQL 函数的归类...

Python文件共享+cpolar内网穿透:轻松实现公网访问

文章目录 1.前言2.本地文件服务器搭建2.1.Python的安装和设置2.2.cpolar的安装和注册 3.本地文件服务器的发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 数据共享作为和连接作为互联网的基础应用&#xff0c;不仅在商业和办公场景有广泛的应用&#…...

Flink之源算子Data Source

源算子Data Source 概述内置Data Source基于集合构建基于文件构建基于Socket构建 自定义Data SourceSourceFunctionRichSourceFunction 常见连接器第三方系统连接器File Source连接器DataGen Source连接器Kafka Source连接器RabbitMQ Source连接器MongoDB Source连接器 概述 Fl…...

在雷电模拟器9上安装magisk并安装LSPosed模块以及其Manager管理器(一)

环境&#xff1a;win10 64&#xff0c;雷电模拟器9.0.60(9)&#xff0c;Android 9。 之前我都是用雷电模拟器版本4.0.78&#xff0c;Android版本7.1.2&#xff0c;为什么本篇要使用9了呢&#xff1f;先解答下这个问题。原因如下&#xff1a;经过我的测试&#xff0c;LSPosed不支…...

Apache atlas 元数据管理治理平台使用和架构

1、前言 Apache Atlas 是托管于 Apache 旗下的一款元数据管理和治理的产品&#xff0c;目前在大数据领域应用颇为广泛&#xff0c;可以很好的帮助企业管理数据资产&#xff0c;并对这些资产进行分类和治理&#xff0c;为数据分析&#xff0c;数据治理提供高质量的元数据信息。…...

MFF论文笔记

论文名称&#xff1a;Improving Pixel-based MIM by Reducing Wasted Modeling Capability_发表时间&#xff1a;ICCV2023 作者及组织&#xff1a;上海人工智能实验室&#xff0c;西门菲沙大学&#xff0c;香港中文大学 问题与贡献 MIM(Model Maksed Model)方法可以分为两部分…...

Leetcode 02.07 链表相交(链表)

Leetcode 02.07 链表相交&#xff08;链表&#xff09; 解法1 尾部对齐解法2&#xff1a;太厉害了&#xff0c;数学归纳推导的方法 很巧妙&#xff0c;这就是将链表的尾端对齐后再一起遍历&#xff0c;这样能满足题目的要求。因为相交之后两个链表到结束的所有节点都一样了&…...

Bootstrap的媒体对象组件(图文展示组件),挺有用的一个组件。

Bootstrap的.media类是用于创建媒体对象的&#xff0c;媒体对象通常用于展示图像&#xff08;图片&#xff09;和文本内容的组合&#xff0c;这种布局在展示新闻文章、博客帖子等方面非常常见。.media类使得创建这样的媒体对象非常简单&#xff0c;通常包含一个图像和相关的文本…...

Day2力扣打卡

打卡记录 无限数组的最短子数组&#xff08;滑动窗口&#xff09; 链接 思路&#xff1a;先求单个数组的总和&#xff0c;再对两个重复数组所组成的新数组上使用 不定长的滑动窗口 来求得满足目标的最小长度。 class Solution { public:int minSizeSubarray(vector<int>…...

项目经理每天,每周,每月的工作清单

很多不懂项目管理的伙伴问&#xff0c;项目经理每天每周每个月的工作是什么呢&#xff1f; 仿佛他们什么都管&#xff0c;但是又没有具体的产出&#xff0c;但是每天看他们比谁都忙&#xff0c;其实很简单&#xff0c;项目中的每个环节负责具体的事情&#xff0c;但是每个环节…...

Java —— 运算符

目录 1. 什么是运算符 2. 算术运算符 2.1 基本四则运算符: 加减乘除模( - * / %) 2.2 增量运算符 - * %与 自增/自减运算符 -- 3. 关系运算符 4. 逻辑运算符 4.1 逻辑与 && 4.2 逻辑或|| 4.3 逻辑非 ! 4.4 短路求值 5. 位运算符 5.1 按位与 & 5.2 按位或 5.3 按位…...

【C++ 中的友元函数:解密其神秘面纱】

友元函数&#xff0c;作为C中一个重要但常常被误解的概念&#xff0c;经常让初学者感到困惑。本文将带您逐步了解友元函数的含义、用途以及如何正确使用它们。 什么是友元函数&#xff1f; 在C中&#xff0c;友元函数是一种特殊的函数&#xff0c;它允许某个类或类的成员函数…...

YOLOv8涨点技巧:手把手教程,注意力机制如何在不同数据集上实现涨点的工作,内涵多种网络改进方法

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;手把手教程&#xff0c;解决注意力机制引入到YOLOv8在自己数据集不涨点的问题点&#xff0c;本文提供五种改进方法来解决此问题&#xff1b; ContextAggregation | 亲测在血细胞检测项目中涨点&#xff0c;…...

牛客:FZ12 牛牛的顺时针遍历

FZ12 牛牛的顺时针遍历 文章目录 FZ12 牛牛的顺时针遍历题目描述题解思路题解代码 题目描述 题解思路 通过一个变量来记录当前方向&#xff0c;遍历矩阵&#xff0c;每次遍历一条边&#xff0c;将该边的信息加入到结果中 题解代码 func spiralOrder(matrix [][]int) []int {…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...