【排序】堆排序详解 附代码

堆排序算法详解 一、堆排序算法原理和动态图解        将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次最大值。如此反复执行,就能得到一个有序序列了。这个过程其实就是先构建一个最大/最小二叉堆,然后不停的取出最大/最小元素(头结点),插入到新的队... 阅读详情

按照国际惯例,开篇前先简单介绍(吹一波)堆排序(Heapsort)。Heapsort是一种优秀的排序算法(个人感觉基本排序算法中仅次于快速排序),时间复杂度为O(nlgn),同时,Heapsort具有空间原址性:任何时候都只需要常数个额外的元素空间存储临时数据。

堆

首先需要明白何谓堆,堆也称为二叉堆,本质上是一个数组,可以被看成一个近似的完全二叉树(完全二叉树是指除了最底层外,该树是完全充满的,而且是从左到右填充)。表示堆的数组A有两个属性:

  • A.length:表征数组元素的个数
  • A.heap-size:表征有多少个数组元素存储在该数组中。
    在这里插入图片描述
    堆一般分为两种形式:最大堆和最小堆。最大堆某个结点的值至多与其父结点一样大,最小堆则恰好相反,最小堆的最小元素存放在根结点中。
    如果把堆看成一棵树,那么堆的高度就可以定义为结点的高度:即该结点到叶结点最长简单路径上边的数目。
    堆排序算法中将会涉及以下三个基本过程:
  • max-heapify过程:时间复杂度O(lgn),维护最大堆性质的关键
  • build-max-heap过程:线性时间复杂度,可以从无序的输入数组中构造一个最大堆
  • heapsort过程:时间复杂度为O(nlgn),功能是对一个数组进行原址排序。

维护堆的性质

维护堆的性质,本质上就是要保证任何一个结点值都要严格大于等于其子结点的值。它的输入是一个数组A和索引i,我们假定根结点为left(i)和right(i)的二叉树都是最大堆,但这时A[i]可能小于其孩子,这就破坏了最大堆的性质。Max-heapify通过让A[i]的值在最大堆中“逐级下降”从而使得以下标i为根结点的子树重新遵循最大堆的性质。
在程序的每一步,从A[i]、A[left(i)]、A[right(i)]中选出最大的,并将其下标存储在largest中。如果A[i]最大,那么以i为根结点的子树已经是最大堆;否则最大元素是i的某个子结点,交换A[i]和A[largest]的值,交换后,下标为largest的结点的值是原来的A[i]。对于每个结点的处理都可以用以下流程图表示:
在这里插入图片描述
但是以该结点为根的子树又可能违反最大堆性质,所以需要按照上述流程堆该子树递归的调用。执行过程如下图所示:
在这里插入图片描述
附维护最大堆性质的代码,Java语言实现:

/***
    * @description 维持最大堆的性质。说明:二叉堆的数组下标从1开始,而此处保留Java的语言习惯,数组从0开始计数
    * @method maxHeapify
    * @params [array, i]
    * @returns void
    * @author Carson Chu
    */
   public static <T extends Comparable<? super T>> void maxHeapify(T[] array, int index) {
       int length = array.length;
       maxHeapify(array, index, length);
   }

   private static <T extends Comparable<? super T>> void maxHeapify(T[] array, int index, int length) {
       int childIndex;
       T curValue;
       for (curValue = array[index]; leftChildIndex(index) < length; index = childIndex) {
           childIndex = leftChildIndex(index);
           if (childIndex != length - 1 && array[childIndex].compareTo(array[childIndex + 1]) < 0) {
               childIndex++;
           }
           if (curValue.compareTo(array[childIndex]) < 0) {
               array[index] = array[childIndex];
           } else {
               break;
           }
       }
       array[index] = curValue;
   }

   /***
    * @description 获取索引为i的作子结点的索引
    * @method leftChildIndex
    * @params [i]
    * @returns int
    * @author Chu Xianglu
    */
   private static int leftChildIndex(int i) {
       return i << 1 + 1;
   }

建堆

读到这里,想必聪明的你就会懂得,为何笔者会先介绍维持堆的性质。没错,建堆的关键就在于保持堆的性质。实际上,我们可以通过自底向上的方法利用过程max-heapify建立最大堆。理论证明,我们永远可以在线性的时间内把一个无序数组构造成一个最大堆。

堆排序

堆排序的重点就在于已经建成的最大堆,我们可以每次删除根结点的值,对剩下的n-1个元素通过max-heapify调整使其重新组成新的最大堆,再删除根结点的值……如此循环往复,直到堆的大小降到2。堆排序的运行过程如下所示:
在这里插入图片描述
堆排序的时间复杂度是O(nlgn),因为每次调用build-max-heap的时间复杂度是O(n),而n-1次调用max-heapify每次的时间复杂度都为O(lgn)。

代码(Java版实现)

/**
 * @author Carson Chu
 * @date 2020/1/5 15:28
 * @description
 */
public class Heapsort<T> {

    /**
     * 标准的堆排序
     *
     * @param a an array of Comparable items.
     */
    public static <T extends Comparable<? super T>>
    void heapsort(T[] a) {
        for (int i = a.length / 2 - 1; i >= 0; i--) {
            /* 建立最大堆 */
            maxHeapify(a, i, a.length);
        }
        for (int i = a.length - 1; i > 0; i--) {
            /* 删除根结点的元素 */
            swapReferences(a, 0, i);
            maxHeapify(a, 0, i);
        }
    }

    /***
     * @description 维持最大堆的性质。说明:二叉堆的数组下标从1开始,而此处保留Java的语言习惯,数组从0开始计数
     * @method maxHeapify
     * @params [array, i]
     * @returns void
     * @author Chu Xianglu
     */
    public static <T extends Comparable<? super T>> void maxHeapify(T[] array, int index) {
        int length = array.length;
        maxHeapify(array, index, length);
    }

    private static <T extends Comparable<? super T>> void maxHeapify(T[] array, int index, int length) {
        int childIndex;
        T curValue;
        for (curValue = array[index]; leftChildIndex(index) < length; index = childIndex) {
            childIndex = leftChildIndex(index);
            if (childIndex != length - 1 && array[childIndex].compareTo(array[childIndex + 1]) < 0) {
                childIndex++;
            }
            if (curValue.compareTo(array[childIndex]) < 0) {
                array[index] = array[childIndex];
            } else {
                break;
            }
        }
        array[index] = curValue;
    }

    /***
     * @description 获取索引为i的作子结点的索引
     * @method leftChildIndex
     * @params [i]
     * @returns int
     * @author Chu Xianglu
     */
    private static int leftChildIndex(int i) {
        return i << 1 + 1;
    }

    /**
     * 交换数组中两个元素的值
     *
     * @param a      an array of objects.
     * @param index1 the index of the first object.
     * @param index2 the index of the second object.
     */
    private static <T> void swapReferences(T[] a, int index1, int index2) {
        T tmp = a[index1];
        a[index1] = a[index2];
        a[index2] = tmp;
    }
}
堆排序详细图解(通俗易懂) 什么是堆 堆是一种叫做完全二叉树的数据结构,可以分为大根堆,小根堆,而堆排序就是基于这种结构而产生的一种程序算法。 堆的分类 大根堆:每个节点的值都大于或者等于他的左右孩子节点的值 小根堆:每个结点的值都小于或等于其左孩子和右孩子结点的值 两种结构映射到数组为: 大根堆: 小根堆: //父-->子:i--->左孩子:2*i+1, 右孩子:2*i+2; //子-->父:i--->(i-1)*2;(i为下标元素) 排序思想 1.首先将待排序的数... 阅读详情

相关推荐

U型槽微带贴片天线:从理论公式到带宽扩展实战

本文深入探讨了U型槽微带贴片天线的设计与带宽扩展实战。针对传统微带天线带宽窄的痛点,详细解析了通过引入U型槽形成双谐振点以拓宽带宽的原理,并提供了从MATLAB理论计算、ANSYS HFSS仿真建模到参数优化的一站式工程指南。文章还分享了多种带宽扩展组合技巧及仿真与实测避坑经验,助力射频工程师设计出高性能的宽带微带天线。

weixin_29168153的博客 447

C语言版堆排序代码讲解(超级详细)

AdjustUp的图解---这个图解的过程得配合HeapPush这个函数看,插入一个,就调整一次,这里的child永远是数组最后一个元素。先说什么是堆呢,堆是一种完全二叉树,它分为大堆和小堆,堆的表示最好用数组表示,因为它是完全二叉树,不存在分支为空。这里我们拿升序的代码举例,记住,升序就要大堆,降序就要小堆,具体为何看代码注释。以此类推,只要记住child永远是数组最后一个,然后插入一次调整一次就行了。这里建议先看代码,代码看不懂再看图解。

weixin_73000974的博客 1713

群晖Hyper Backup还原实战:加密与非加密备份的完整操作指南

本文详细介绍了群晖Hyper Backup在加密与非加密备份场景下的完整还原操作指南。通过实战案例和高级技巧,帮助用户应对硬盘故障或系统崩溃时的数据恢复挑战,特别是加密备份恢复中的关键节点和常见陷阱。

weixin_29323365的博客 440

堆排序详解

堆排序详解

Xpccccc的博客 2130

【编号326】全国地灾数据全国25万多个地质灾害点空间分布数据

全国地质灾害点分布数据涵盖25万多个各类灾害点信息,为灾害防治提供重要依据。通过分析灾害分布规律,可识别高风险区,指导监测预警、应急避险和基础设施建设。多地成功案例(如湖北兴山崩塌避险、重庆长寿滑坡预警、湖南"挪床行动")证明,提前预警和科学防治能有效避免人员伤亡。云南省2024年通过预警体系成功避让31起灾害,保护1342人生命安全。这些实践表明,地质灾害防治需结合科技手段与群防体系,实现早预警、早处置。

千奇百怪无奇不有 1472

电赛2024年H题智能小车基于MSPM0G3507主控MCU(利用8路灰度加上MPU6050的解决方式)

最后我觉得这个项目最重要的部分还是陀螺仪的精准度,如果一个陀螺仪的精准度高,这个项目就十分简单了,像mpu6050这种陀螺仪精准度就稍微差了一点,他有个最严重的问题就是温漂,偏航角会随着温度变化为变化,这一点在跑发挥项一的时候十分关键的,因为这个只差基督就会导致车子跑偏,导致无法完成 整个项目。如果条件足够的话建议可以换一个好一点的陀螺仪,然后就是灰度如果没有条件同样也可以换成红外。以上就是本次项目的一些小建议。

weixin_60991529的博客 6243

堆排序(一)——建小堆(含向下调整代码)

在不额外开辟空间的情况下,通过向下调整来建一个小堆

challenglistic的博客 2704

optee技术简介

OP-TEE(Open Portable Trusted Execution Environment)是一种开源的可信执行环境(TEE),专为ARM TrustZone架构设计。TEE是一种独立的执行环境,运行在主操作系统之外,为敏感任务提供安全的计算空间,如加密、身份验证、密钥管理等。

baron-周贺贺-代码改变世界ctw 1279

排序算法:堆排序,快速排序,归并排序。内附完整代码和算法思路详解。

第一次归并1和5,使得1,5有序, 第二趟归并1,5,6,使得1,5,6,有序。 第三趟归并4,7,使得4,7,有序。 第二趟归并1,5,6,和4,7使得1,4,5,6,7,有序。

qq_51004011的博客 1163

【2026年华为暑期实习(AI)-4月22日-第三题- 网络异常流量传播链路溯源】(题目+思路+Java&C++&Python解析+在线测试)

本文提出了一种基于空间距离和流量负载的网络异常传播链检测方法。首先通过曼哈顿距离建立基站间的直接关联关系,形成无向图;然后计算各连通块的总负载,筛选出关键节点;最后在关键节点构成的有向无环图(DAG)中寻找用户数最大的传播路径。算法采用DFS/BFS识别连通块,并通过动态规划求解最长路径,时间复杂度为O(N^2)。该方法适用于监控网络中异常流量的定向传播分析,能有效识别高负载区域和最大影响路径。

m0_73659489的博客 187

学Simulink--机器人基础关节控制场景实例:基于Simulink的无刷直流电机(BLDC)关节六步换相位置控制仿真

本文详细介绍了基于Simulink的BLDC六步换相位置控制仿真方法,针对机器人关节的低成本控制需求。重点阐述了霍尔定位、六步换相和位置闭环三大核心原理,通过Simulink建模实现了从霍尔信号处理到位置环控制的全流程。仿真结果表明,该系统可实现0.3°的定位精度和80ms的响应时间,满足教育机器人等应用场景需求。文章还提供了核心代码模块和参数设置方法,并探讨了转矩脉动抑制、无传感器控制等进阶优化方向,为低成本机器人关节驱动提供了实用解决方案。

amy_mhd的博客 152

堆排序详解(附可运行代码)

堆排序 /** * parent = (i-1)/2; * son1 = 2*i + 1; * son2 = 2*i + 2; */ #include<iostream> #include<vector> using namespace std; void swap(vector<int> &tree, int node1, int ...

长期更新自学笔记 476

硬件工程师入门基础知识(一)基础元器件认识(二)

我们的电路输入一般是只有两种状态,0V 或者是其它的高电平(1.8V,3.3V,5V 等),截止状态一般不用怎么考虑,因为如果让三极管的 Vbe=0,自然就截止了,重要的是饱和状态如何保证。也就是说只要 Rb

小幽余生不加糖 1万+

数据结构 堆排序(Heap Sort) 详解 附C++代码实现:

目录 简介: 算法描述: 代码实现: 总结: 简介: 堆排序是利用堆这种数据结构所设计的排序算法,是一种近似于完全二叉树。分为大顶堆和小顶堆。时间复杂度为:o(nlog2(n)),不稳定。 算法描述: 知识储备:一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1...

小渣渣的博客 6119

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

详解堆排序 附代码 无序数组中找到最小的k个数

堆排序: 堆排序时间复杂度是O(NlogN) 空间复杂度如果不用递归的话可以达到O(1),在数组上原地排序 这里给出堆排序代码 注意此处在筛操作的时候sift(Object[] a, int n, int i),中间的参数n是代表这时候数组的总大小。因为在建完堆后,数组从后往前会一轮一轮的有序,n会渐渐减小。在无序数组中找最小的k个数的代码中就不需要记录这个值,在这代码中,需要建大根堆,用

qq_31617121的博客 710

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

排序算法之堆排序详解(附最大堆示例代码)

1.堆的概念(最大堆与最小堆) 堆排序的时间复杂度是n logN,具有nl og N时间复杂度的排序算法还有快速排序和理想状态下的归并排序,一般来说快速排序的性能优于堆排序,但堆排序在实际中也有很多的应用,后续会做具体介绍. 堆指的是堆数据结构,被看成是一个近似的完全二叉树,树上的每一个结点对应数组中的一个元素. 完全二叉树满足两个特性: 1.父结点的键值总是

刘旭 3127

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

希尔排序与堆排序超详解(附理解性实验与指导+完整C代码)

希尔排序、堆排序算法总结摘要希尔排序堆排序实验指导 /* 先送一个漂亮的排序算法,给有缘读到本文的读者 这个算法是研究希尔排序的增量序列时偶然发现的, 与希尔排序的原理类似,但发生了质变。 就叫“七上八下排序”吧。。 */ void sort(int* arr,int length){ int gap=length,temp; while(gap){//间距为零退出 for(int i=0;i+gap<length;++i) {//真就一层循环,O(n)

weixin_46216553的博客 955

七大排序算法详解——排序(二)选择排序(附Java代码,堆排序流程图解)

所谓排序,就是使一串记录,按照其中的某个或某几个关键字的大小,递增或递减排列起来的操作。:在待排序的记录序列中,存在多个具有相同关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的。:把数据全部加载到内存中进行排序。:数据太多,内存不能存储了,将数据放到磁盘、U盘上进行排序。

m0_63793861的博客 232

【C语言数据结构】堆的实现与堆排序详解(附完整代码)

在之前的数据结构学习中,我们掌握了线性表(顺序表、链表)、栈和队列。这些结构处理数据时,要么按插入顺序(栈/队列),要么按位置(线性表)。但是否存在一种结构,能自动将数据按优先级(如大小)组织,每次都能高效地取出“最大”或“最小”的元素?堆(Heap)正是为此而生!它是一种特殊的完全二叉树,通常用数组实现,能在O(log n)时间内插入和删除极值,在O(1)时间内获取极值。它也是优先队列的底层实现,广泛应用于堆排序、Top-K 问题、Dijkstra 算法等。本文将带你从零实现一个。

2603_95011023的博客 355

经典排序算法之堆排序详解|c++代码实现|什么是堆排序|如何代码实现堆排序

经典排序算法之堆排序详解|c++代码实现|什么是堆排序|如何代码实现堆排序

qq_64431512的博客 3095

详解堆排序

堆排序 1.堆排序基本介绍 (1) 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。 (2) 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。 (3) 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆 (4) 大顶堆举例说明: 我们对堆中的结点按层进行编号,映射到数组中就是下面这个样子: 大顶堆特点:arr[

xiaoshiguang3的博客 3109

【排序】详解堆排序

具体实现如下:首先我们需要从最后一个孩子所在的子堆进行调整,最好一个孩子就是数组长度-1的位置,那么我们如何根据孩子节点的下标去求出父亲节点的下标呢,我们只需要让孩子节点的下标减去1的差然后除2就能得到父亲节点的下标,同样我们知道父亲节点的下标同样除2在加1就能求出孩子节点的下标。首先我们需要找到孩子节点中的最大值,然后拿他与父亲节点比较,如果他比父亲节点大,那么就进行交换,然后依次向下进行调整(调整孩子节点的子堆),如果比父亲节点小说明这个堆已经调整完成我们就可以结束向下。重复此操作将最大值交换到末尾。

qq_61903414的博客 1084
上一篇: 【MySQL】深入理解为什么MySQL使用B+树
下一篇: 【MySQL】一次线上的OR查询导致的索引失效 附解决方案
少侠露飞
博客等级 码龄9年 214粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值