堆排序(浅谈大顶堆与小顶堆)

2023-11-15

什么是堆?

堆是一种非线性结构,(本篇随笔主要分析堆的数组实现)可以把堆看作一个数组,也可以被看作一个完全二叉树,通俗来讲堆其实就是利用完全二叉树的结构来维护的一维数组,按照堆的特点可以把堆分为大顶堆和小顶堆。

  • 大顶堆:每个结点的值都大于或等于其左右孩子结点的值
  • 小顶堆:每个结点的值都小于或等于其左右孩子结点的值

堆的特点

在这里插入图片描述
我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
在这里插入图片描述
我们用简单的公式来描述一下堆的定义就是:(读者可以对照上图的数组来理解下面两个公式)

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

堆和普通树的区别

内存占用

普通树占用的内存空间比它们存储的数据要多。你必须为节点对象以及左/右子节点指针分配额外的内存。堆仅仅使用数组,且不使用指针

(可以使用普通树来模拟堆,但空间浪费比较大,不太建议这么做)

平衡

二叉搜索树必须是“平衡”的情况下,其大部分操作的复杂度才能达到O(nlog2n)。你可以按任意顺序位置插入/删除数据,或者使用 AVL 树或者红黑树,但是在堆中实际上不需要整棵树都是有序的。我们只需要满足对属性即可,所以在堆中平衡不是问题。因为堆中数据的组织方式可以保证O(nlog2n) 的性能

搜索

在二叉树中搜索会很快,但是在堆中搜索会很慢。在堆中搜索不是第一优先级,因为使用堆的目的是将最大(或者最小)的节点放在最前面,从而快速的进行相关插入、删除操作

堆排序的过程

先了解下堆排序的基本思想:

将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值,

如此反复执行,便能得到一个有序序列了,建立最大堆时是从最后一个非叶子节点开始从下往上调整的(这句话可能不好太理解),下面会举一个例子来理解堆排序的基本思想

给一个无序序列如下,int a[6] = {7, 3, 8, 5, 1, 2},现在可以根据数组将完全二叉树还原出来
在这里插入图片描述
好了,现在我们要做的事情就是要把7,3,8,5,1,2变成一个有序的序列,如果想要升序就是1,2,3,5,7,8 如果想要降序就是8,7,5,3,2,1 ,这两种就是我们要的最终结果,然后我们就可以根据我们想要的结果来选择

适合类型的堆来进行排序

  • 升序----使用大顶堆
  • 降序----使用小顶堆

为什么升序要用大顶堆呢?

上面提到过大顶堆的特点:每个结点的值都大于或等于其左右孩子结点的值,我们把大顶堆构建完毕后根节点的值一定是最大的,然后把根节点的和最后一个元素(也可以说最后一个节点)交换位置,那么末尾元素此时就是最大元素了(理解这点很重要)

知道了堆排序的原理下面就可以来操作了,在进行操作前先理清一下步骤(假设我们想要升序的排列)

  1. 先n个元素的无序序列,构建成大顶堆
  2. 将根节点与最后一个元素交换位置,(将最大元素"沉"到数组末端)
  3. 交换过后可能不再满足大顶堆的条件,所以需要将剩下的n-1个元素重新构建成大顶堆
  4. 重复第二步、第三步直到整个数组排序完成

图解交换过程(得到升序序列,使用大顶堆来调整)
这里以int a[6] = {7, 3, 8, 5, 1, 2}为例子

先要找到最后一个非叶子节点,数组的长度为6,那么最后一个非叶子节点就是:长度/2-1,也就是6/2-1=2,然后下一步就是比较该节点值和它的子树值,如果该节点小于其左\右子树的值就交换(意思就是将最大的值放到该节点)

8只有一个左子树,左子树的值为2,8>2不需要调整
在这里插入图片描述
下一步,继续找到下一个非叶子节点(其实就是当前坐标-1就行了),该节点的值为3小于其左子树的值,交换值,交换后该节点值为5,大于其右子树的值,不需要交换

下一步,继续找到下一个非叶子节点,该节点的值为7,大于其左子树的值,不需要交换,再看右子树,该节点的值小于右子树的值,需要交换值

下一步,检查调整后的子树,是否满足大顶堆性质,如果不满足则继续调整(这里因为只将右子树的值与根节点互换,只需要检查右子树是否满足,而8>2刚好满足大顶堆的性质,就不需要调整了,

如果运气不好整个数的根节点的值是1,那么就还需要调整右子树)

到这里大顶堆的构建就算完成了,然后下一步交换根节点(8)与最后一个元素(2)交换位置(将最大元素"沉"到数组末端),此时最大的元素就归位了,然后对剩下的5个元素重复上面的操作

剩下只有5个元素,最后一个非叶子节点是5/2-1=1,该节点的值(5)大于左子树的值(3)也大于右子树的值(1),满足大顶堆性质不需要交换

在这里插入图片描述
找到下一个非叶子节点,该节点的值(2)小于左子树的值(5),交换值,交换后左子树不再满足大顶堆的性质再调整左子树,左子树满足要求后再返回去看根节点,根节点的值(5)小于右子树的值(7),再次交换值

得到新的大顶堆,如下图,再把根节点的值(7)与当前数组最后一个元素值(1)交换,再重构大顶堆->交换值->重构大顶堆->交换值····,直到整个数组都变成有序序列

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

堆排序(浅谈大顶堆与小顶堆) 的相关文章

  • docker从入门到实战

    Docker Docker 基础 Docker 容器与镜像的概念 容器 Container 容器是一种可执行的虚拟化环境 它包含了应用程序以及运行此应用程序所需的所有依赖项 包括软件 库 配置等 容器只存在于主机操作系统的内存中 不占用独立

随机推荐

  • vue中函数回调传值

    在业务开发中 有时候会碰到一种情况 组件内部会触发一些事件用来请求新的数据 数据请求到后将新的数据继续在组件内进行处理 这个场景我们有三种方式可以实现它 将数据请求写在组件内部 缺点不好维护 违反了单项数据流的原则 通常我们可以通过this
  • js验证邮箱的正则表达式

    最近小小研究了一下正则表达式 觉得写正则表达式还挺有意思的 先想推荐一个网址 把正则表达式的基本语法都总结了 很不错 https msdn microsoft com zh cn library ae5bf541 v vs 100 aspx
  • SiliconLab GSDK 4.2.2 创建 Z3Gateway 工程

    如题 在SSv5的My Product选项下 需要添加Linux 32 Bit 否则在Example Projects Demos中无法查找到zigbeegateway相关的demo My Product 中的内容添加成功后 再次搜索Exa
  • 剑指 Offer 29. 顺时针打印矩阵(java+python)

    输入一个矩阵 按照从外向里以顺时针的顺序依次打印出每一个数字 示例 1 输入 matrix 1 2 3 4 5 6 7 8 9 输出 1 2 3 6 9 8 7 4 5 示例 2 输入 matrix 1 2 3 4 5 6 7 8 9 10
  • Anchor Base 和 Anchor Free

    1 概念 1 1 什么是Anchor Anchor也叫做锚 其实是一组预设的边界框用于在训练时学习真实的边框位置相对于预设边框的偏移 通俗点说就是预先设置目标可能存在的大概位置 然后再在这些预设边框的基础上进行精细化的调整 而它的本质就是为
  • npm run build 打包后,如何查看效果

    我们用vue cli搭建的项目执行npm build后本地打开页面空白 如果才能查看npm run build之后的结果呢 首先我们看一下提示 Tip built files are meant to be served over an H
  • 手机第一次怎么充电?

    来说说电池保养问题 现在手机用的大概是600 1800毫安时左右的锂电池 而锂电池是没有记忆的 可以随时随地充电 电池和耳机一样有一个煲的过程 刚买回来的时候电池的功效不会发挥到最大 刚买回来用的前两次一定要把电量用完再充 这点很重要 大概
  • LayUI系列(三)之动态添加选项卡

    文章目录 前言 一 什么是Tab选项卡 二 动态选项卡 1 通过网站查找layui选项卡的页面布局代码 2 动态添加选项卡 3 将选项卡的名称换成选中的菜单名称 4 已打开的选项卡不再重复打开 5 选择已被打开的选项卡则进行选项卡的转换 三
  • JS中的prototype

    JS中的prototype JS中的phototype是JS中比较难理解的一个部分 本文基于下面几个知识点 1 原型法设计模式 在 Net中可以使用clone 来实现原型法 原型法的主要思想是 现在有1个类A 我想要创建一个类B 这个类是以
  • AB实验核心知识点总结

    一 AB实验的价值以及应用场景 AB实验目前已经是互联网以及泛互联网行业的标配 像字节这样的互联网大厂 元气森林这样的饮料公司都把AB实验作为产品功能迭代的重要的工具 它之所以如此重要是因为其能给出定性因果和定量增长两种核心价值 让产品快速
  • Pycharm在Debug时,Tensor张量显示不全问题

    问题 在Debug时 当我们需要对某个张量查看它里面各个数值 只显示开头和结尾的部分数据 出现张量显示不全 解决方法 对要查看到数据按下鼠标右键 选择 评估表达式 的选项 对要查看的数据 先转换到cpu上 再转换到numpy 最后 点击 评
  • react学习总结

    目录 1 react生命周期 2 关于组件 className 设置的问题 3 react 中实现一些动画的效果 4 encodeURIComponent 5 react项目开发步骤推荐 6 webpack 的特色与功能 1 react生命
  • 22年菲尔兹奖获得者HUGO DUMINIL-COPIN 研究内容总结

    雨果 迪米尼 科潘获得 2022 的菲尔兹数学奖 因解决了统计物理中相变 概率理论的长期问题 特别是三维和四维问题以及二维中的不可积情况 下 雨果最显著的成果是三维和四维 Ising 模型 他与合作者解决了 80 年代以来一直存在的问题 建
  • 03使用Spring基于XML的方式注册第一个组件

    基于XML的方式注册第一个组件 开发步骤 第一步 创建Maven工程配置生成的pom xml文件 添加spring context基础依赖和junit依赖 注意根据Spring官方文档描述 Spring6需要JDK版本17 当添加Sprin
  • windows环境下启动kafka的

    1 命令 bin windows kafka server start bat config server properties
  • vs2013配置CUDA .cu文件

    1 Linker中添加相应lib 主要有cublas lib cublas device lib cuda lib cudadevrt lib cudart lib cudart static lib 2 右键项目 gt Bulid Dep
  • json转对象

    JSON parseObject方法可以实现json转化为对象 public class Test1 public static void main String args String jsonStr n code 200 n data
  • 机器学习笔试面试超详细总结(四)

    文章目录 151 Ridge回归 Lasso回归 坐标下降法求解 152 电影推荐系统是以下哪些的应用实例 153 决策树是否可以用来聚类 可以 154 什么方法最适合于在n维空间做异常点检测 155 逻辑回归和多元回归分析的不同 156
  • 人工智能芯片发展 (1

    人工智能技术随着以深度学习为核心算法的大力发展 目前已经在场景识别 语音识别等方面迅猛发展 影响人工智能的三大要素 数据 算法 算力 其中算力 是实现算法的重要基础 人工智能芯片也处于这个时代的战略至高点 目前人工智能 芯片分为三类 a A
  • 堆排序(浅谈大顶堆与小顶堆)

    什么是堆 堆是一种非线性结构 本篇随笔主要分析堆的数组实现 可以把堆看作一个数组 也可以被看作一个完全二叉树 通俗来讲堆其实就是利用完全二叉树的结构来维护的一维数组 按照堆的特点可以把堆分为大顶堆和小顶堆 大顶堆 每个结点的值都大于或等于其