Hello算法8:堆

Hello算法8:堆

定义

堆heap是满足特定条件的完全二叉树(只有最底层节点未填满,且节点靠左填充),主要有以下两种:

大顶堆:任意节点的值≥其子节点的值

小顶堆:任意节点的值≤子节点的值

在这里插入图片描述

堆的常用操作

方法名描述时间复杂度
push()元素入堆O(log⁡n)
pop()堆顶元素出堆O(log⁡n)
peek()访问堆顶元素(对于大 / 小顶堆分别为最大 / 小值)O(1)
size()获取堆的元素数量O(1)
isEmpty()判断堆是否为空O(1)

在实际应用中,我们可以直接使用编程语言提供的堆类(或优先队列类)。

# 初始化小顶堆
min_heap, flag = [], 1
# 初始化大顶堆
max_heap, flag = [], -1

# Python 的 heapq 模块默认实现小顶堆
# 考虑将“元素取负”后再入堆,这样就可以将大小关系颠倒,从而实现大顶堆
# 在本示例中,flag = 1 时对应小顶堆,flag = -1 时对应大顶堆

# 元素入堆
heapq.heappush(max_heap, flag * 1)
heapq.heappush(max_heap, flag * 3)
heapq.heappush(max_heap, flag * 2)
heapq.heappush(max_heap, flag * 5)
heapq.heappush(max_heap, flag * 4)

# 获取堆顶元素
peek: int = flag * max_heap[0] # 5

# 堆顶元素出堆
# 出堆元素会形成一个从大到小的序列
val = flag * heapq.heappop(max_heap) # 5
val = flag * heapq.heappop(max_heap) # 4
val = flag * heapq.heappop(max_heap) # 3
val = flag * heapq.heappop(max_heap) # 2
val = flag * heapq.heappop(max_heap) # 1

# 获取堆大小
size: int = len(max_heap)

# 判断堆是否为空
is_empty: bool = not max_heap

# 输入列表并建堆
min_heap: list[int] = [1, 3, 2, 5, 4]
heapq.heapify(min_heap)

堆的实现

  1. 索引和节点的关系

    由下图可知数组索引和节点的关系:

    给定一个节点的索引是i,它的左子节点是2i+1,右子节点是2i+2,父节点是(i-1)/2取整除

    在这里插入图片描述

    def left(i: int) -> int:
        """获取左子节点的索引"""
        return 2 * i + 1
    
    def right(i: int) -> int:
        """获取右子节点的索引"""
        return 2 * i + 2
    
    def parent(i: int) -> int:
        """获取父节点的索引"""
        return (i - 1) // 2  # 向下整除
    
  2. 访问堆顶元素

    显然堆顶元素就是max_heap[0]

    def peek(self) -> int:
            """访问堆顶元素"""
            return self.max_heap[0]
    
  3. 元素入堆

    首先将元素加入堆底部,由于元素的值可能大于堆中的元素,堆的结构会被破坏。所以我们需要来执行一遍检查和调整,将新元素放到合适的位置,这个操作就叫做「堆化 heapify」

    def push(self, val: int):
            """元素入堆"""
            # 添加节点
            self.max_heap.append(val)
            # 从底至顶堆化
            self.sift_up(self.size() - 1)
    
        def sift_up(self, i: int):
            """从节点 i 开始,从底至顶堆化"""
            while True:
                # 获取节点 i 的父节点
                p = self.parent(i)
                # 当“越过根节点”或“节点无须修复”时,结束堆化
                if p < 0 or self.max_heap[i] <= self.max_heap[p]:
                    break
                # 交换两节点
                self.swap(i, p)
                # 循环向上堆化
                i = p
    
  4. 堆顶元素出堆

    由于直接删除数组首元素会改变所有数组的索引,这将破坏整个堆的结构,这会增加很多堆化操作的工作量,所以我们用以下步骤来进行

    • 交换堆顶元素和堆底元素(也就是交换数组第一个元素和最后一个元素)
    • 交换完成后,删除堆底元素(由于在上一步已经进行交换,实际上删除的是堆顶元素)
    • 从根元素开始,从顶至底,执行堆化

堆的应用

  • 优先队列
  • 堆排序
  • 选取最大的k个元素,比如销量最高的商品,热度最高的新闻等

建堆操作

很多时候,我们需要通过一个数组来建立一个堆,这就叫做建堆。

很容易想到的一个建堆的方式,就是先建立一个空堆,然后遍历数组,把数组元素加入堆尾部,然后再进行堆化操作。这样很容易理解,但是效率不高,时间复杂度是nlogn。

实际上一般用下面的方法进行建堆操作:

  1. 首先,将数组所有元素加入到堆
  2. 倒序遍历堆,依次对每个非叶节点,执行从顶到底的堆化操作
def __init__(self, nums: list[int]):
        """根据输入列表建堆"""
        self.max_heap = nums
        for i in range(self.parent(self.size()-1), -1, -1):
            self.sift_down(i)

经数学证明,这种方式的时间复杂度是O(n)

堆的应用-Topk问题

返回数组中最大的k个元素

方法一:遍历选择

对数据进行遍历,每次找到一个第n大的元素。时间复杂度是O(kn),如果k比较接近n,时间复杂度是O(n^2)

方法二:排序

显然我们可以对数组排序,然后返回后K个元素。这种算法显然超额完成任务了,因为并不需要对数组所有元素进行排序

方法三:借助堆实现

思路如下:

建立一个小顶堆,堆顶元素为最小

先将数组前k个元素依次入堆

从第k+1个元素开始,若当前元素大于堆顶元素,堆顶元素出堆,该元素入堆

遍历完成后,堆中保存的就是最大的k个元素

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

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

相关文章

最小覆盖子串-java

最小覆盖子串-java 题目描述 : 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 "" 。 注意&#xff1a; 对于 t 中重复字符&#xff0c;我们寻找的子字符串中该字符数量必…

阿里云2核4G云服务器支持多少人同时在线?并发数计算?

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

语音识别:基于HMM

HMM语音识别的解码过程 从麦克风采集的输入音频波形被转换为固定尺寸的一组声学向量&#xff1a; 其中是维的语音特征向量&#xff08;例如MFCC&#xff09;。 解码器尝试去找到上述特征向量序列对应的单词&#xff08;word&#xff09;的序列&#xff1a; 单词序列的长度是。…

HAProxy + Vitess负载均衡

一、环境搭建 Vitess环境搭建&#xff1a; 具体vitess安装不再赘述&#xff0c;主要是需要启动3个vtgate&#xff08;官方推荐vtgate和vtablet数量一致&#xff09; 操作&#xff1a; 在vitess/examples/common/scripts目录中&#xff0c;修改vtgate-up.sh文件&#xff0c;…

计算机网络——32差错检测和纠正

差错检测和纠正 错误检测 EDC 差错检测和纠错位&#xff08;冗余位&#xff09; D 数据由差错检测保护&#xff0c;可以包含头部字段 错误检测不是100%可靠的 协议会泄露一些错误&#xff0c;但是很少更长的EDC字段可以得到更好的检测和纠正效果 奇偶校验 单bit奇偶校验 …

opejdk11 java 启动流程 java main方法怎么被jvm执行

java启动过程 java main方法怎么被jvm执行 java main方法是怎么被jvm调用的 1、jvm main入口 2、执行JLI_Launch方法 3、执行JVMInit方法 4、执行ContinueInNewThread方法 5、执行CallJavaMainInNewThread方法 6、创建线程执行ThreadJavaMain方法 7、执行ThreadJavaMain方法…

YOLOv9改进策略 :主干优化 | ConvNeXtV2:适应自监督学习,让 CNN “再一次强大”?

💡💡💡本文改进内容:完全卷积掩码自编码器框架 ConvNeXt V2,它显著提高了纯convnet在各种识别基准上的性能,包括ImageNet分类,COCO目标检测和ADE20k分割。还提供了各种尺寸的预训练ConvNeXt v2模型,从而在ImageNet上具有76.7%精度的3.7M Atto model和88.9%精度的650…

CrossOver软件2024免费 最新版本详细介绍 CrossOver软件好用吗 Mac电脑玩Windows游戏

CrossOver是一款由CodeWeavers公司开发的软件&#xff0c;它可以在Mac和Linux等操作系统上运行Windows软件&#xff0c;而无需在计算机上安装Windows操作系统。这款软件的核心技术是Wine&#xff0c;它是一种在Linux和macOS等操作系统上运行Windows应用程序的开源软件。 Cross…

本地虚拟机服务器修改站点根目录并使用域名访问的简单示例

说明&#xff1a;本文提及效果是使用vmware虚拟机&#xff0c;镜像文件是Rocky8.6 一、配置文件路径 1. /etc/httpd/conf/httpd.conf #主配置文件 2. /etc/httpd/conf.d/*.conf #调用配置文件 调用配置文件的使用&#xff1a; vim /etc/httpd/conf.d/webpage.conf 因为在主配…

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】

1、SPI协议 简介 SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface)&#xff0c;即串行外围设备接口&#xff0c;是 一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间&#xff0c;要求通讯速率 较高的场合。 SPI 物理层 SPI 通讯…

【讲解下Docker in Docker的原理与实践】

&#x1f308;个人主页:程序员不想敲代码啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

elementUI this.$msgbox msgBox自定义 样式自定义 富文本

看这个效果是不是很炫?突出重点提示内容,对于用户交互相当的棒! 下来说说具体实现: let self = this const h = self.$createElement; this.$msgbox({title: null,message: h("p", {style: "margin-top:10px"}, [h("i", {class: "el-i…

Linux——将云服务器作为跳板机,frp实现内网穿透

文章目录 操作步骤1. 准备工作&#xff1a;2. 配置frp服务器端&#xff1a;3. 配置frp客户端&#xff1a;4. 启动frp客户端&#xff1a;5. 测试连接&#xff1a;6. 安全注意事项&#xff1a; 云服务器性能分析阿里云具体操作步骤1. 购买&#xff1a;2. 登录&#xff1a;3. 首次…

Redis 慢日志

Redis慢日志 1.Redis 慢查询日志概述 客户端从发送命令到获取返回结果经过了以下几个步骤&#xff1a; 客户端发送命令该命令进入 Redis 队列排队等待执行Redis 开始执行命令 - Redis 命令执行完成命令执行结果返回给客户端 Redis 慢查询日志统计的时间&#xff0c;只包含第…

Docker 哲学 - compose.yaml 指令

compose.yaml 的 image commond working_dir 和 dockerfile的 from cmd workdir 区别在哪里 。为什么 dockerfile制定过了。compose还要再写一个。是处于个性化还是 有不同的意义 如果 dockerfile 的 from 是 node:16 &#xff0c;compose.yaml 的 images 是 node:18 那么 直接…

杰发科技——Jlink插件使用

0. 简介 杰发自带的烧录工具是ATCLink&#xff0c;基于DapLink适配。个人不太喜欢ATCLink&#xff0c;推荐使用Jlink&#xff0c;毕竟自己买&#xff0c;不用问原厂要&#xff0c;而且带Jlink&#xff0c;至少5Mhz以上。 V9烧录器使用7.50以下版本驱动。 V11烧录器可以使用7…

生信数据分析——GO+KEGG富集分析

生信数据分析——GOKEGG富集分析 目录 生信数据分析——GOKEGG富集分析1. 富集分析基础知识2. GO富集分析&#xff08;Rstudio&#xff09;3. KEGG富集分析&#xff08;Rstudio&#xff09; 1. 富集分析基础知识 1.1 为什么要做功能富集分析&#xff1f; 转录组学数据得到的基…

Java中将字符串的指定部分赋值给另一个字符串

正如标题所示&#xff0c;不说废话&#xff0c;直接上代码&#xff1a; public class test{public static void main(String[] args) {String str1 "我真的会谢谢你";/*原则是左闭右开&#xff0c;左边是起始下标&#xff0c;右边是终止下标字符串的下标是从0开始*…

【微服务】软件架构的演变之路

目录 单体式架构的时代单体式架构(Monolithic)优点缺点适用场景单体式架构面临诸多问题1.宽带提速&#xff0c;网民增多2.Web2.0时代的特点问题描述优化方向 集群优点缺点适用场景搭建集群后面临诸多问题用户请求问题用户的登录信息数据查询 改进后的架构 垂直架构优点缺点 分布…

python opencv之提取轮廓并拟合圆

图片存储地址为&#xff1a;C:\Users\Pictures\test.png&#xff0c;该图像图片背景是黑色的&#xff0c;目标区域是亮的&#xff0c;目标区域是两段圆弧和两段曲线构成的封闭区域&#xff0c;其中两段圆弧属于同一个圆&#xff0c;但在目标区域的相对位置&#xff0c;也就是不…