深入浅出排序算法之堆排序

目录

1. 算法介绍

2. 执行流程⭐⭐⭐⭐⭐✔

3. 代码实现

4. 性能分析


1. 算法介绍

堆是一种数据结构,可以把堆看成一棵完全二叉树,这棵完全二叉树满足:任何一个非叶结点的值都不大于(或不小于)其左右孩子结点的值。若父亲大孩子小,则这样的堆叫作大顶堆;若父亲小孩子大,则这样的堆叫作小顶堆。

根据堆的定义知道,代表堆的这棵完全二叉树的根结点的值是最大(或最小)的,因此将一个无序序列调整为一个堆,就可以找出这个序列的最大(或最小)值,然后将找出的这个值交换到序列的最后(或最前),这样,有序序列关键字增加1个,无序序列中关键字减少1个,对新的无序序列重复这样的操作,就实现了排序。这就是堆排序的思想。

堆排序中最关键的操作是将序列调整为堆。整个排序的过程就是通过不断调整,使得不符合堆定义的完全二叉树变为符合堆定义的完全二叉树。

2. 执行流程⭐⭐⭐⭐⭐✔

建堆是先从自下而上,从右往左建

初始堆的每一个结点都要满足堆的定义,也就是父节点的值大于左右孩子结点的值!!!

选出最大值,是将根结点和最后一个结点互换,然后继续构建大顶堆!!!

⭐⭐⭐堆顶和最后一个元素交换,才算一趟,也是该趟的最终序列结果!!!

建堆和排序结果是两个阶段,但同属于一趟中。

图示如下:

3. 代码实现

为了三个步骤:

步骤一:先建堆(大根堆或者小根堆)

步骤二:交完堆顶和最后一个元素,然后堆的大小减一

步骤三:向下调整堆

步骤一只需实现一次,步骤二和步骤三循环执行,得到最终的有序序列。

    //开始排序:堆排序分为三个功能 ①开始建堆,②交换,③向下调整,重复②和③步public static void heapSort(int[] array,int len){int end = len - 1;//确定最后一个结点的下标createHeap(array);//建堆//当只剩下一个结点的时候,就不需要交换while(end > 0){//交换swap(array,0,end);//向下调整shiftDown(array,0,end);//调整完一个结点,下一个end--;}}//交换数据public static void swap(int[] array,int i,int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}//堆排序(大根堆)//从上往下建堆,所以先找父节点,再找孩子结点public static void createHeap(int[] array){for(int parent = (array.length - 1 - 1) / 2;parent >= 0;parent--){shiftDown(array,parent,array.length);}}//向下调整public static void shiftDown(int[] array,int parent,int len){//定义一个记录孩子下标的变量(左孩子)int child = 2 * parent + 1;//判断父节点和孩子结点的大小,至少左孩子要存在while(child < len){//比较左右孩子if((child + 1) < len && array[child] < array[child + 1]){child++;}//判断父节点和孩子节点if(array[child] > array[parent]){swap(array,child,parent);parent = child;child = 2 * parent + 1;}else{break;}}}
    public static void main(String[] args) {int[] a = {5,4,3,2,1};Sort.heapSort(a, a.length);for (int x : a) {System.out.print(x + " ");}}

4. 性能分析

时间辅助度空间复杂度
O(N*logN)O(1)
数据不敏感数据不敏感

稳定性:不稳定。

来上解析,怎么计算这个时间复杂度。

(1)步骤一的时间复杂度:首先知道有N个结点开始建堆,这个时间复杂度就是O(N),大家可以去看看这篇文章,里面有讲建堆的时间复杂度。链接如下:

数据结构——堆、堆排序和优先级队列(代码为Java版本)

(2)步骤二和步骤三循环的时间复杂度:那么我第一个结点交换时,需要向下调整为log(N - 1)层;交换第二个结点后,需要向下log(N - 2),接下来就是log(N - 3),log(N - 4),……,log1。所以总的调整次数是log(N - 1) + log(N - 2) + log(N - 3) + log(N - 4) + …… + log1 = log((N - 1)!)。

我们可以在网上看到堆排序的时间复杂度是O(N*logN),这是堆排序的大致估算(我们算时间复杂度都是算个大概),其实log((N - 1)!) 约等于 NlogN。下面是我的证明结果:

① 使用夹逼准则证明:

先求上限:\log \left ( n!\right ) = \sum_{i = 1}^{n}\log \left ( i \right )\leqslant \sum_{i=1}^{n}\log \left ( n \right )=\log n^{n}=O\left (n\log n \right )

再求下限:

因为 n! \geqslant \left ( \frac{n}{2} \right )^{\frac{n}{2}}

所以 \log \left ( n! \right )\geqslant \log \left ( \frac{n}{2} \right )^{\frac{n}{2}}= \frac{n}{2}\log \frac{n}{2}= \frac{n}{2}\log n-\frac{n}{2}\log 2

当 n\geqslant 4 时,\frac{n}{2}\log 2=\frac{1}{4}n\log 4\leqslant \frac{1}{4}n\log n               

② 则有:

\log \left ( n! \right )\geqslant \frac{n}{2}\log n-\frac{n}{2}\log 2\geqslant \frac{n}{2}\log n-\frac{1}{4}n\log n=\frac{1}{4}n\log n\approx \Omega \left ( n\log n \right )     

③结论:\log \left ( n! \right ) 既是 n\log n 的低阶函数,又是 n\log n 的高阶函数,因此是 n\log n 的同阶函数!

(3)由于上面的证明步骤,我们可以知道堆排序的时间复杂度是  O\left ( n\log n \right ) 。

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode题:1:两数之和

&#xff08;1&#xff09;、题目要求&#xff1a; &#xff08;2&#xff09;、解题思路&#xff1a; 由上图可知&#xff0c;target 20&#xff0c;这题有多种解法 1、暴力枚举的方法&#xff1a; 但是这样时间复杂度就很高了&#xff0c;要定义两个下标&#xff0c;列举出所…

Proteus仿真--基于51单片机的LED模拟交通灯仿真(仿真文件+程序)

本文主要介绍基于51单片机的LED模拟交通灯仿真&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真运行视频 Proteus仿真--基于51单片机的LED模拟交通灯仿真&#xff08;仿真文件程序&#xff09; 附完整Proteus仿真资料代码资料 百度网盘链接: https://pan.baidu.c…

uniapp开发小程序—根据生日日期计算年龄 周岁

0、需求 在UniApp开发小程序中&#xff0c;将接口返回的出生日期转化为年龄&#xff1b;判断接口返回的年龄是否是周岁 可以使用JavaScript的日期处理方法来实现。 一、第一种方式&#xff08;示例代码&#xff09;&#xff1a; //javascript // 假设接口返回的年龄为生日的…

ES(elasticsearch) - 三种姿势进行分页查询

1. from size 浅分页 "浅"分页可以理解为简单意义上的分页。它的原理很简单&#xff0c;就是查询前20条数据&#xff0c;然后截断前10条&#xff0c;只返回10-20的数据。这样其实白白浪费了前10条的查询。 GET test_dev/_search {"query": {"bool&…

vscode配置C/C++

首先下载 https://sourceforge.net/projects/mingw-w64/files/mingw-w64/ 解压到一个方便的文件夹中 在环境变量中添加 C:\mingw64\bin验证添加的环境变量&#xff0c;打开cmd&#xff0c;输入gcc --version 进入vscode进行配置 安装插件 进行配置 gcc和g的配置文件…

7-1、S曲线加减速原理【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;本节介绍步进电机S曲线相关内容&#xff0c;总共分四个小节讨论步进电机S曲线相关内容   根据上节内容&#xff0c;步进电机每一段的速度可以任意设置&#xff0c;但是每一段的速度都会跳变&#xff0c;当这个跳变值比较大的时候&#xff0c;电机会发生明显的…

Builder 请进:波卡最新开发入门指南

撰文&#xff1a;Dennis Zoma 编译&#xff1a;OneBlock 社区 本文更新于 2023 年 10 月 3 日&#xff0c;来源&#xff1a;https://wiki.polkadot.network/docs/build-guide Polkadot 是一个区块链协议&#xff0c;有两个目标&#xff1a;在所有连接的平行链之间提供共享安全…

java基础 特殊文件

1.Properties属性文件&#xff1a; 1.1使用Properties读取属性文件里的键值对数据&#xff1a; package specialFile;import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Enumeration; import java.util.Propert…

水性杨花:揭秘CSS响应式界面设计,让内容灵活自如,犹如水之变幻

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、是…

SOLIDWORKS参数化设计之格式转换 慧德敏学

现在越来越多的企业开始进行模型的参数化设计规范&#xff0c;不管是使用SOLIDWORKS自带的方程式&#xff0c;还是使用SOLIDWORKS参数化设计插件&#xff0c;参数化的过程其实已经很透明了&#xff0c;都大同小异。 我们之前介绍过SolidKits.AutoWorks软件&#xff0c;可以很方…

【多线程面试题 六】、 如何实现线程同步?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a; 如何实现线程同步&…

JVM进阶(1)

一)JVM是如何运行的&#xff1f; 1)在程序运行前先将JAVA代码转化成字节码文件也就是class文件&#xff0c;JVM需要通过类加载器将字节码以一定的方式加载到JVM的内存运行时数据区&#xff0c;将类的信息打包分块填充在运行时数据区&#xff1b; 2)但是字节码文件是JVM的一套指…