深入解析数据结构与算法之堆

news2024/9/25 15:29:45

文章目录

    • 🥦引言:
    • 🥦什么是堆
    • 🥦大顶堆与小顶堆
      • 🧄大顶堆(Max Heap)
      • 🧄小顶堆(Min Heap)
    • 🥦堆的表示
      • 🧄数组表示:
      • 🧄树表示:
    • 🥦堆的操作
      • 🧄堆化操作
      • 🧄插入操作
      • 🧄删除根节点操作
      • 🧄堆的创建
    • 🥦堆的应用
      • 🧄优先队列
      • 🧄堆排序
      • 🧄辅助数据结构
    • 🥦堆的复杂度分析
    • 🥦结论
    • 🥦参考文献

🥦引言:

在计算机科学中,数据结构和算法是构建复杂软件系统的基石。堆作为一种经典的数据结构,具有广泛的应用和重要的算法基础。本文将深入解析堆的原理、性质和常见的操作,帮助读者更好地理解和应用堆。

🥦什么是堆

堆是一种特殊的数据结构,属于树的一种表现形式。堆具有以下两个主要特征:

  • 堆是一个完全二叉树(或近似完全二叉树):堆中的所有层次都被填满,最后一层从左到右填入节点。

  • 堆具有堆序性:对于最小堆来说,父节点的值始终小于或等于其子节点的值;对于最大堆来说,父节点的值始终大于或等于其子节点的值。

  • 在堆中,每个节点的值都取决于其子节点的值:对于最小堆来说,父节点的值不大于任何子节点的值;对于最大堆来说,父节点的值不小于任何子节点的值。

🥦大顶堆与小顶堆

对于堆来说,可以根据节点的性质分为两种类型:大顶堆(Max Heap)和小顶堆(Min Heap)。

🧄大顶堆(Max Heap)

在大顶堆中,父节点的值大于或等于其子节点的值。也就是说,大顶堆的根节点是堆中的最大值。对于任意节点i,其子节点的值必须小于或等于节点i的值。大顶堆常用于获取最大值或进行部分排序。
结构表示如下:
在这里插入图片描述

🧄小顶堆(Min Heap)

在小顶堆中,父节点的值小于或等于其子节点的值。也就是说,小顶堆的根节点是堆中的最小值。对于任意节点i,其子节点的值必须大于或等于节点i的值。小顶堆常用于获取最小值或进行部分排序。
结构表示如下:
在这里插入图片描述

无论是大顶堆还是小顶堆,堆的逻辑结构和操作都是相同的。唯一的区别是节点的值大小和根节点的值。

在实际应用中,大顶堆和小顶堆都有各自的用途。例如,在优先队列中,可以使用大顶堆来实现,以便快速获取优先级最高的元素。而在进行部分排序时,可以使用小顶堆来实现,以方便获取最小的k个元素。

无论是大顶堆还是小顶堆,它们都是一种非常重要的数据结构,在算法和数据处理中有广泛的应用。了解堆的性质和操作,可以帮助我们更好地理解和应用这两种堆。

🥦堆的表示

堆可以使用数组或树来表示。下面分别介绍这两种表示方法:

🧄数组表示:

  • 在数组表示中,堆的元素按照完全二叉树的形式存储在一个数组中。

  • 堆的根节点存储在数组的索引位置0处。

  • 对于索引为i的节点,其左子节点的索引为2i + 1,右子节点的索引为2i + 2。

  • 通过数组的索引关系,可以方便地在堆的插入、删除等操作中定位到对应的节点。

例如,对于一个大顶堆(Max Heap)的数组表示:heap = [9, 6, 7, 3, 5, 1, 4],可以构建如下的堆结构:
在这里插入图片描述
数组表示:

在这里插入图片描述

🧄树表示:

  • 在树表示中,堆可以使用二叉树进行表示。

  • 堆的根节点是二叉树的根节点。

  • 每个节点最多有两个子节点,并且子节点与父节点之间有特定的大小关系(对于大顶堆是大于等于,对于小顶堆是小于等于)。

  • 通过指针或引用,可以方便地在堆的插入、删除等操作中定位到对应的节点。

  • 例如,对于一个大顶堆(Max Heap)的树表示:
    在这里插入图片描述

🥦堆的操作

🧄堆化操作

首先引入堆化的概念, 当我们向一个已经是堆的数据结构(如数组或二叉堆)中插入一个新元素时,需要进行插入操作,并保持堆的性质。这个过程可以称为"堆插入"。堆插入的一般步骤如下:

将新元素插入到堆的最后一个位置(或数组的末尾)。
向上调整(也称为上浮)新插入的元素,直到它在堆中找到合适的位置并满足堆的性质。
具体来说,对于大顶堆(Max Heap)的情况,堆插入的过程如下:

将新元素插入到堆的最后一个位置。
与其父节点进行比较,如果新元素的值大于父节点的值,则交换它们的位置。
重复上述步骤,直到新元素找到了合适的位置并满足堆的性质。

类似地,对于小顶堆(Min Heap)的情况,堆插入的过程如下:

将新元素插入到堆的最后一个位置。
与其父节点进行比较,如果新元素的值小于父节点的值,则交换它们的位置。
重复上述步骤,直到新元素找到了合适的位置并满足堆的性质。

通过堆插入操作,可以在保持堆的性质下有效地将新元素插入到堆中,并且时间复杂度为O(log n),其中n表示堆的大小。插入完成后,堆将继续保持堆的性质。

🧄插入操作

向堆中插入一个新节点。通常,插入操作首先将新节点添加到堆的末尾,然后通过向上调整(上滤)操作来恢复堆的堆序性。
示例:

  1. 初始状态
    在这里插入图片描述

  2. 插入元素12
    插入12后,需要将其与父节点5进行比较,因为12大于5,所以交换它们的位置。
    在这里插入图片描述

  3. 插入12后,需要将其与父节点5进行比较,因为12大于5,所以交换它们的位置。然后,还需要将12与其父节点8进行比较,因为12大于8,所以交换它们的位置。最后,还需要将12与其父节点10进行比较,因为12大于10,所以交换它们的位置。
    在这里插入图片描述


代码示例

def heapify_up(heap, index):
    parent_index = (index - 1) // 2
    while parent_index >= 0 and heap[index] > heap[parent_index]:
        heap[index], heap[parent_index] = heap[parent_index], heap[index]
        index = parent_index
        parent_index = (index - 1) // 2

def insert_into_heap(heap, new_item):
    heap.append(new_item)
    heapify_up(heap, len(heap) - 1)

# 示例使用:
heap = [10,8,7,5,6,3,4]
print("原始堆:", heap)

insert_into_heap(heap, 9)
print("插入后的堆:", heap)

🧄删除根节点操作

将堆中的根节点删除。通常,删除根节点后,将堆中最后一个节点移到根位置,然后通过向下调整(下滤)操作来恢复堆的堆序性。
下面是一个示例的大顶堆删除操作的结构图:

  1. 初始状态:

在这里插入图片描述

  1. 删除堆顶元素12:

在这里插入图片描述

删除堆顶元素时,需要将最后一个元素5替换到堆顶,然后通过向下调整操作,将其移动到合适的位置,并保持大顶堆的性质。

  1. 向下调整操作:

在这里插入图片描述

在向下调整的过程中,将当前节点与其子节点进行比较,如果子节点的值较大,则将当前节点与较大的子节点交换位置。继续以上比较和交换的步骤,直到当前节点不再有子节点或者当前节点的值大于等于其子节点的值,保持大顶堆的性质。

  1. 最终结果:

在这里插入图片描述

在删除堆顶元素的过程中,需要进行多次比较和交换,并通过向下调整操作将新的堆顶元素移动到正确的位置,同时保持大顶堆的性质。以上是一个简单的示例,实际操作中可能还需要考虑边界情况和特殊情况的处理。

下面是一个示例的 Python 代码,展示了如何执行堆的删除操作:

import heapq

# 创建一个堆
heap = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

# 将列表转换为堆
heapq.heapify(heap)

# 删除堆顶元素
root = heapq.heappop(heap)

print("删除的堆顶元素:", root)
print("删除后的堆:", heap)

输出如下:

删除的堆顶元素: 1
删除后的堆: [2, 3, 4, 3, 5, 9, 5, 6, 5]

在上述代码中,使用了 heapq 模块提供的函数来执行堆操作。heapify 函数将普通列表转换为堆,heappop 函数用于删除堆顶元素,并返回被删除的元素。最后,打印删除的堆顶元素和删除后的堆。

🧄堆的创建

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
  
int array[] = {27,15,19,18,28,34};
在这里插入图片描述


在这里插入图片描述

🥦堆的应用

🧄优先队列

使用堆来实现优先队列,可以快速找到最大或最小的元素,并在一系列数据中动态调整优先级。

🧄堆排序

堆排序是一种高效的排序算法,利用堆的性质进行排序操作。

🧄辅助数据结构

堆在其他算法和数据结构中的实现中起到辅助作用,如图的最短路径算法中使用的Dijkstra算法。

🥦堆的复杂度分析

堆的插入和删除操作的时间复杂度均为O(log n),其中n为堆中元素的数量。堆化操作的时间复杂度为O(n)。

🥦结论

堆作为一种重要的数据结构,在计算机科学中广泛应用。通过深入理解堆的原理、性质和操作,我们能够更好地应用堆解决实际问题。堆不仅作为优先队列和排序算法的基础,还在各种算法和系统中发挥着重要的作用。熟练掌握堆的概念和操作,将极大地提高算法设计和实现的能力。

🥦参考文献

Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2009). Introduction to algorithms. MIT press.

🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄


🏫博客主页:魔王-T

🏯系列专栏:结构算法

🥝大鹏一日同风起 扶摇直上九万里

❤️感谢大家点赞👍收藏⭐评论✍️


END

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

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

相关文章

智能座舱架构与芯片- (8) 视觉篇

一、概述 相比起用于ADAS感知系统的摄像头,用于智能座舱内部的摄像头,其功能特性和性能要求相对简单。例如,OMS乘客监控摄像头,一般达到5MP即可有良好的效果。同时,OMS也可应用于车内会议系统,还应用于车内…

利用OpenCV实现图片中导线的识别

下面是一个需求,识别图片中的导线,要在图像中检测导线,我们需要采用不同于直线检测的方法。由于OpenCV没有直接的曲线检测函数,如同它对直线提供的HoughLines或HoughLinesP,检测曲线通常需要更多的图像处理步骤和算法&…

多线程的概念

点击链接返回标题-> 什么是进程? 进程(Process),是程序的基本执行实体。 在早期面向进程设计的计算机结构中,进程是程序的基本执行实体; 在当代面向线程设计的计算机结构中,进程是线程的容器…

上海亚商投顾:沪指冲高回落 短剧、地产股集体走强

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 三大指数早盘冲高,创业板指盘初涨超1%,午后则集体下行翻绿,北证50一度大涨…

MyBatis:关联查询

MyBatis 前言关联查询附懒加载对象为集合时的关联查询 前言 在 MyBatis:配置文件 文章中,最后介绍了可以使用 select 标签的 resultMap 属性实现关联查询,下面简单示例 关联查询 首先,先创建 association_role 和 association_…

win11,引导项管理

1,打开cmd,输入msconfig 2,进入引导选项卡 3,删除不需要的引导项

Go语言常用命令详解(三)

文章目录 前言常用命令go get示例参数说明 go install示例参数说明 go list示例 go mod示例参数说明 go work基本用法示例 go tool示例 go version示例 go vet示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命…

支付宝生僻字选择器

本文的数据来源于支付宝网页版本生僻字选择器。 let rareWords[{spell: "a",words: ["奡", "靉", "叆"]}, {spell: "b",words: ["仌", "昺", "竝", "霦", "犇", "愊…

mysql底层是如何存放数据的

总览 首先总的来说,分为四个层级,行页区段。行就是数据库里的一行数据。 但一次从磁盘读进内存的数据量是一页(页是读写的单位,默认16KB一页),页分很多种类,例如数据页、溢出页、undo日志页。 …

Rust语言精讲:数据类型全解析

大家好!我是lincyang。 今天,我们将深入探讨Rust语言中的数据类型,这是理解和掌握Rust的基础。 Rust语言数据类型概览 Rust是静态类型语言,所有变量类型在编译时确定。Rust的数据类型分为两类:标量类型和复合类型。…

909-2014-T3

文章目录 1.原题2.算法思想3.关键代码4.完整代码5.运行结果 1.原题 有n个顶点的无向图,使用邻接矩阵作为存储结构。为减少存储空间,使用数组按照行主映射方式仅保存下三角矩阵。请给出映射公式,并编写算法计算给定顶点的度。叙述算法思想并用…

基于ubuntu20.04安装ros系统搭配使用工业相机

基于ubuntu20.04安装ros系统搭配使用工业相机 1. ROS系统安装部署1.1更新镜像源1.1.1 备份源文件1.1.2 更新阿里源1.1.3 更新软件源 1.2 ros系统安装1.2.1 添加ros软件源1.2.2 添加秘钥1.2.3 更新软件源1.2.4 配置及更换最佳软件源1.2.5 ROS安装1.2.6 初始化rosdep1.2.7 设置环…

【Hello Go】Go语言文本文件处理

文本文件处理 字符串处理字符串操作ContainsJoinindexrepeatReplaceSplitTrimFields 字符串转换AppendFormatParse 正则表达式Json处理编码Json通过结构体生产Json通过map生产json 解码Json解析到结构体解析到interface 文件操作相关api介绍建立和打开文件关闭文件写文件读文件…

基于Apache部署虚拟主机网站

文章目录 Apache释义Apache配置关闭防火墙和selinux 更改默认页内容更改默认页存放位置个人用户主页功能基于口令登录网站虚拟主机功能基于ip地址相同ip不同域名相同ip不同端口 学习本章完成目标 1.httpd服务程序的基本部署。 2.个人用户主页功能和口令加密认证方式的实现。 3.…

Linux02 VIM编辑器

Linux02 VIM编辑器 基本上 vi/vim 共分为三种模式, 分别是命令模式(Command mode),输入模式(Insert mode)和底线命令模式(Last line mode)。 三种状态进行切换 插入模式&#xff1a…

【机器学习】039_合理初始化

一、稳定训练 目标:使梯度值在更合理的范围内 常见方法如下: 将乘法变为加法 ResNet:当层数较多时,会加入一些加法进去 LSTM:如果时序序列较长时,把一些对时序的乘法做加法 归一化 梯度归一化&…

Java(五)(Object类,克隆,Objects类,包装类,StringBuilder,StringJoiner,BigDecimal)

目录 Object类 Object类的常见方法: 克隆 浅克隆 深克隆 Objects类 包装类 StringBuilder StringJoiner BigDecimal Object类 Object类是java中的祖宗类,因此,Java中所有的类的对象都可以直接使用object类提供的一些方法 Object类的常见方法: public String toStrin…

Redis-Redis持久化,主从哨兵架构详解

Redis持久化 RDB快照(snapshot) 在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。 你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数…

Mac- Iterm 2 (替换mac terminal 终端的下一代终端工具)

1.应用场景 主要用于macOS中作为终端工具使用, 执行终端命令, Iterm 2 (替换mac terminal 终端的下一代终端工具) 2.学习/操作 1.文档阅读 chatgpt & 其他资料 2.整理输出 2.1 是什么 Iterm 2 (替换mac terminal 终端的下一代终端工具) 2.2 为什么需要「应用场景」 macOS…

自定义类型之结构体

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - :来于“云”的“羽球人”。…