【转载】堆排序算法(图解详细流程)

article/2025/10/29 22:39:29

堆排序的时间复杂度O(N*logN),额外空间复杂度O(1),是一个不稳定性的排序

目录

一 准备知识

1.1  大根堆和小根堆

二 堆排序基本步骤

2.1 构造堆

2.2 固定最大值再构造堆

三 总结

四 代码

 


一 准备知识

堆的结构可以分为大根堆和小根堆,是一个完全二叉树,而堆排序是根据堆的这种数据结构设计的一种排序,下面先来看看什么是大根堆和小根堆

1.1  大根堆和小根堆

性质:每个结点的值都大于其左孩子和右孩子结点的值,称之为大根堆;每个结点的值都小于其左孩子和右孩子结点的值,称之为小根堆。如下图

 我们对上面的图中每个数都进行了标记,上面的结构映射成数组就变成了下面这个样子

还有一个基本概念:查找数组中某个数的父结点和左右孩子结点,比如已知索引为i的数,那么

1.父结点索引:(i-1)/2(这里计算机中的除以2,省略掉小数)

2.左孩子索引:2*i+1

3.右孩子索引:2*i+2

所以上面两个数组可以脑补成堆结构,因为他们满足堆的定义性质:

大根堆:arr(i)>arr(2*i+1) && arr(i)>arr(2*i+2)

小根堆:arr(i)<arr(2*i+1) && arr(i)<arr(2*i+2)

二 堆排序基本步骤

基本思想:

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端

2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1

3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

2.1 构造堆

将无序数组构造成一个大根堆(升序用大根堆,降序就用小根堆)

假设存在以下数组

主要思路:第一次保证0~0位置大根堆结构(废话),第二次保证0~1位置大根堆结构,第三次保证0~2位置大根堆结构...直到保证0~n-1位置大根堆结构(每次新插入的数据都与其父结点进行比较,如果插入的数比父结点大,则与父结点交换,否则一直向上交换,直到小于等于父结点,或者来到了顶端)

插入6的时候,6大于他的父结点3,即arr(1)>arr(0),则交换;此时,保证了0~1位置是大根堆结构,如下图:

                                     (友情提示:待交换的数为蓝色,交换后的数为绿色)

 插入8的时候,8大于其父结点6,即arr(2)>arr(0),则交换;此时,保证了0~2位置是大根堆结构,如下图

插入5的时候,5大于其父结点3,则交换,交换之后,5又发现比8小,所以不交换;此时,保证了0~3位置大根堆结构,如下图 

插入7的时候,7大于其父结点5,则交换,交换之后,7又发现比8小,所以不交换;此时整个数组已经是大根堆结构 

 

2.2 固定最大值再构造堆

此时,我们已经得到一个大根堆,下面将顶端的数与最后一位数交换,然后将剩余的数再构造成一个大根堆

                                    (友情提示:黑色的为固定好的数字,不再参与排序) 

 此时最大数8已经来到末尾,则固定不动,后面只需要对顶端的数据进行操作即可,拿顶端的数与其左右孩子较大的数进行比较,如果顶端的数大于其左右孩子较大的数,则停止,如果顶端的数小于其左右孩子较大的数,则交换,然后继续与下面的孩子进行比较

下图中,5的左右孩子中,左孩子7比右孩子6大,则5与7进行比较,发现5<7,则交换;交换后,发现5已经大于他的左孩子,说明剩余的数已经构成大根堆,后面就是重复固定最大值,然后构造大根堆

如下图:顶端数7与末尾数3进行交换,固定好7,

剩余的数开始构造大根堆 ,然后顶端数与末尾数交换,固定最大值再构造大根堆,重复执行上面的操作,最终会得到有序数组

 

三 总结

到这里,大家应该对堆排序都有了自己的见解,我们对上面的流程总结下:

1、首先将无需数组构造成一个大根堆(新插入的数据与其父结点比较)

2、固定一个最大值,将剩余的数重新构造成一个大根堆,重复这样的过程

四 代码

代码中主要两个方法:

1、将待排序数组构造成一个大根堆(元素上升)

2、固定一个最大值,将剩余的数再构造成一个大根堆(元素下降)

    //堆排序public static void heapSort(int[] arr) {//构造大根堆heapInsert(arr);int size = arr.length;while (size > 1) {//固定最大值swap(arr, 0, size - 1);size--;//构造大根堆heapify(arr, 0, size);}}//构造大根堆(通过新插入的数上升)public static void heapInsert(int[] arr) {for (int i = 0; i < arr.length; i++) {//当前插入的索引int currentIndex = i;//父结点索引int fatherIndex = (currentIndex - 1) / 2;//如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点//然后继续和上面的父结点值比较,直到不大于父结点,则退出循环while (arr[currentIndex] > arr[fatherIndex]) {//交换当前结点与父结点的值swap(arr, currentIndex, fatherIndex);//将当前索引指向父索引currentIndex = fatherIndex;//重新计算当前索引的父索引fatherIndex = (currentIndex - 1) / 2;}}}//将剩余的数构造成大根堆(通过顶端的数下降)public static void heapify(int[] arr, int index, int size) {int left = 2 * index + 1;int right = 2 * index + 2;while (left < size) {int largestIndex;//判断孩子中较大的值的索引(要确保右孩子在size范围之内)if (arr[left] < arr[right] && right < size) {largestIndex = right;} else {largestIndex = left;}//比较父结点的值与孩子中较大的值,并确定最大值的索引if (arr[index] > arr[largestIndex]) {largestIndex = index;}//如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环if (index == largestIndex) {break;}//父结点不是最大值,与孩子中较大的值交换swap(arr, largestIndex, index);//将索引指向孩子中较大的值的索引index = largestIndex;//重新计算交换之后的孩子的索引left = 2 * index + 1;right = 2 * index + 2;}}//交换数组中两个元素的值public static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}

转载:https://guguoyu.blog.csdn.net/article/details/81283998


http://chatgpt.dhexx.cn/article/kVn44ygb.shtml

相关文章

堆排序算法(图解详细流程)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 堆排序基本介绍大顶堆举例说明小顶堆举例说明堆排序的基本思想堆排序步骤图解说明堆排序的基本思路总结 堆排序基本介绍 堆排序是利用堆这种数据结构而设计的一种排…

堆排序详细图解(通俗易懂)

什么是堆 堆是一种叫做完全二叉树的数据结构&#xff0c;可以分为大根堆&#xff0c;小根堆&#xff0c;而堆排序就是基于这种结构而产生的一种程序算法。 堆的分类 大根堆:每个节点的值都大于或者等于他的左右孩子节点的值 小根堆:每个结点的值都小于或等于其左孩子和右孩子…

PreferenceActivity(二)

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我正好又在项目中用到&#xff0c;所以就把自己的使用的在这总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; Android系统截图&#xff08;左&#xff09; …

android设置页面之PreferenceActivity及Preference

离上篇博客刚好一周&#xff0c;希望后面会记录更多的内容&#xff0c;也算自己的Android笔记吧。 本篇主要记录一般的android设置页面PreferenceActivity的使用以及与之剪不断&#xff0c;理还乱的Preference。 &#xff08;一&#xff09;如何使用 Android系统自带的设置应用…

android中PreferencesActivity的使用(一)

在使用android手机的时候&#xff0c;尤其是在操作软件设置时&#xff0c;我们经常见到这样的界面&#xff1a; 这是怎么来实现的的呢&#xff1f;其实android已经提供了相应的类和方法&#xff0c;当进行简单数据存储时&#xff08;比如&#xff1a;软件配置参数&#xff09;a…

PreferenceActivity用法简介(二)

本测试主要是为了测试PreferenceActivity的使用&#xff0c;其中设置了播放背景音乐和开启wifi的设置&#xff0c;也就是本文要讲的 PreferenceActivity。 Android提供了放摆放的工具来定义所有的程序的首选项&#xff0c;并支持既不不许要编写代码的情况写显示这些首选项。可…

Android之PreferenceActivity

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我在看Android Samples时无意中看见了&#xff0c;所以就稍微总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; 好了&#xff0c;我们看到Android系统本身就大…

Android PreferenceActivity使用

PreferenceActivity继承了ListActivity&#xff0c;定义Activity继承PreferenceActivity。在res目录下新建一个xml文件夹&#xff0c;接着在这个文件夹下新建一个取名为preferences.xml的File文件&#xff0c;xml中可以使用的标签(Tag)可以分为两类&#xff0c;一类是管理布局的…

PreferenceActivity定制

android很多设置界面都会使用PreferenceActivity来实现&#xff0c;但那个界面比较丑陋&#xff0c;显示开发总是满足不了要求。 可以自己实现一个&#xff0c;但是那样又会使Activity中的逻辑代码和xml布局文件过于复杂&#xff0c;远远不及PreferenceActivity来的方便快捷。…

PreferenceActivity、PreferenceFragment使用

目录 目录前言PreferenceActivity preferences_scenario_1xmlPreference Activity演示 PreferenceFragment xml布局文件Preference FragmentPreference Activity管理Fragment 适配 前言 转来转去又回到了Android&#xff0c;闲话少说&#xff0c;这里是参考Android原生的Setti…

Android PreferenceActivity的介绍

转载&#xff1a;http://blog.chinaunix.net/uid-24666775-id-351136.html 在Android中的APIdemos是中经常遇到过继承于PreferenceActivity这个类&#xff0c;紧接着就是addPreferencesFromResource(R.xml.*******);&#xff08;附&#xff1a;这个******就是一个XML文件&#…

Android开发--详解SharedPreference/PreferenceActivity

我们经常看到应用程序的设置页面&#xff0c;一般用到设置页面时&#xff0c;我们会继承自PreferenceActivity&#xff0c;它实现了SharedPreference&#xff0c;并生成相应的XML文件自动保存用户的设置&#xff0c;在设置页面中&#xff0c;每一个列表项都是一个Preference&am…

Android之PreferenceActivity 详解

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我正好又在项目中用到&#xff0c;所以就把自己的使用的在这总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; Android系统截图&#xff08;左&#xff09; …

android的PreferenceActivity

前言 这段时间在研究android平台上的开源项目——StandupTimer&#xff0c;这是由jwood所设计的一个较为简单android应用&#xff0c;用于控制会议时间&#xff0c;类似秒表倒计时。 PreferenceActivity PreferenceActivity是android提供的对系统信息和配置进行自动保存的Activ…

用StopWatch统计耗时,比System.currentTimeMillis好用

平时项目中统计耗时都用System.currentTimeMillis&#xff0c;最近看到一个spring-StopWatch统计耗时&#xff0c;其用法简单明了&#xff0c;比传统统计耗时方法好用。 StopWatch 的内部是通过 System.nanoTime() 来计时的&#xff0c;本质和 System.currentTimeMillis() 差别…

并发慎用——System.currentTimeMillis()

好记忆不如烂笔头&#xff0c;能记下点东西&#xff0c;就记下点&#xff0c;有时间拿出来看看&#xff0c;也会发觉不一样的感受. System.currentTimeMillis()是极其常用的基础Java API&#xff0c;广泛地用来获取时间戳或测量代码执行时长等&#xff0c;在我们的印象中应该快…

System.currentTimeMillis()的性能问题以及解决方法

System.currentTimeMillis()是极其常用的基础Java API&#xff0c;广泛地用来获取时间戳或测量代码执行时长等&#xff0c;在我们的印象中应该快如闪电。但实际上在并发调用或者特别频繁调用它的情况下&#xff08;比如一个业务繁忙的接口&#xff0c;或者吞吐量大的需要取得时…

统计代码执行时间时,System.currentTimeMillis()与System.nanoTime()哪个更适合?

目录 1.nanoTime是什么&#xff1f; 2.currentTimeMillis是什么&#xff1f; 3.nanoTime与currentTimeMillis在JDK中阐述 4.nanoTime与currentTimeMillis使用对比 5.深究从OpenJDK源代码、glibc&#xff0c;一直到Linux内核 6.总结 1.nanoTime是什么&#xff1f; ns&…

System.currentTimeMillis的性能如何

一、背景 撸代码时发现System.currentTimeMillis的调用都被封装成了cache类型&#xff0c;代码如下: 那么System.currentTimeMillis真的有这么这么差吗&#xff0c;如果差的话又是什么原因造成的&#xff1f;什么情况下可以直接调用原生方法&#xff0c;什么情况下需要使用缓存…

别再用 System.currentTimeMillis 统计耗时了,太 Low,试试 Spring Boot 源码在用的 StopWatch吧,够优雅

大家好&#xff0c;我是二哥呀&#xff01; 昨天&#xff0c;一位球友问我能不能给他解释一下 SpringBootApplication 注解是什么意思&#xff0c;还有 Spring Boot 的运行原理&#xff0c;于是我就带着他扒拉了一下这个注解的源码&#xff0c;还有 SpringApplication 类的 ru…