十大排序算法之堆排序

堆排序

在简单选择排序文章中,简单选择排序这个“铁憨憨”只顾着自己做比较,并没有将对比较结果进行保存,因此只能一遍遍地重复相同的比较操作,降低了效率。针对这样的操作,Robertw.Floyd 在1964年提出了简单选择排序的升级版——堆排序方法。
堆是什么呢?堆是用数组实现的已标号的完全二叉树。

1. 算法思想

在讲算法思想前,先解释几个基本知识点。就像上文所说的:用数组实现的已标号的完全二双树称之为堆。如果父节点的键值均不小于子节点,则为大顶堆;如果父节点的键值均不大于子节点,则为小顶堆,如下图所示。
在这里插入图片描述
圆圈旁边的数字即为节点的索引,如果我们按照这个索引将节点的逻辑结构映射到数组中,就变成了如下图所示的存储结构。
在这里插入图片描述

我们再用两个公式简单地描述一下节点之间的关系。
大顶堆: a r r [ i ] ≥ a r r [ 2 i + 1 ] arr[i] \geq arr[2i+1] arr[i]arr[2i+1] && a r r [ i ] ≤ a r r [ 2 i + 2 ] arr[i] \leq arr[2i+2] arr[i]arr[2i+2]
小顶堆: a r r [ i ] ≤ a r r [ 2 i + 1 ] arr[i] \leq arr[2i+1] arr[i]arr[2i+1] && a r r [ i ] ≥ a r r [ 2 i + 2 ] arr[i] \geq arr[2i+2] arr[i]arr[2i+2]
如果大家看懂了这两个公式,那么就会理解堆排序的基本思想:一次又一次地将待排序数组构造成一个大顶堆,然后一次又一次地将大顶堆的根节点(最大值)和最末尾的元素交换位置并将最末尾的元素隔离,直到整个序列变得有序。

2. 算法步骤

假设初始序列的堆结构如下图所示。
在这里插入图片描述

(1)将待排序数组构建成一个大顶堆(若降序排列,则采用小顶堆)。
为了构建大顶堆,我们需要从最后一个非叶子节点开始先从左到右,再从上到下地进行调整。最后一个非叶子节点的计算公式为:
a r r . l e n g t h 2 − 1 = 6 2 − 1 = 2 \frac{arr.length}{2}-1=\frac{6}{2}-1=2 2arr.length1=261=2
即为“2”节点,由于8>2,所以将二者交换,如下图所示。
在这里插入图片描述
找到第二个非叶子节点“5”,因为[5,1,3]中5最大,所以无须进行交换。第三个非叶子节点为“1”,因为[1,5,8]中8最大,所以1和8交换,如下图所示。
在这里插入图片描述我们发现,这次交换后右子树又被打乱了,2比1大,因此需要再更新一下,如下图所示。
在这里插入图片描述

这样,我们成功地将待排序数组构建成第一个大顶堆。
在这里插入图片描述
(3)重复步骤(1)和(2),直到整个序列变得有序。
重新调整数组结构,使其满足大顶堆的结构,如下图所示。
在这里插入图片描述
然后继续交换堆顶和堆底的元素,又“沉”了一个,如下图所示。
在这里插入图片描述
接下来都是类似操作,就这样一直执行,直到整个数组变得有序,如下图所示。
在这里插入图片描述

3. 算法分析

有的读者肯定有疑问,为什么在经过步骤(1)和步骤(2),进行了四五次比较和交换的操作后,得到的有序数组意然和开始的待排序数组是一样的,都是[1,5,2,1,3,8]呢?其实这也是堆排序的一个不足之处。那么我们如何最大化地提升堆排序的效率呢?这个问题就交给大家去思考吧。
堆排序的思想总结起来有两点:构建堆结构+交换堆顶和堆底元素。构建第一个大顶堆时,时间复杂度为O(n)之后还有n-1次的交换元素和交换之后堆的重建,根据完全二叉树的性质来说,操作次数应该是呈 l o g ( n − 1 ) , l o g ( n − 2 ) , l o g ( n − 3 ) , ⋯ , 1 log(n-1),log(n-2),log(n-3),\cdots,1 log(n1),log(n2),log(n3),,1的态势逐步递减的,也就近似为 O ( l o g 2 n ) O(log_2 n) O(log2n)。因此,不难得出,堆排序的时间复杂度为 O ( n l o g n ) O(nlog {\ }n) O(nlog n)
同样,当待排序数组是逆序时,就是最坏的情况。这时候不仅需要进行 O ( n l o g n ) O(nlog {\ }n) O(nlog n)复杂度的比较操作,还需要进行 O ( n l o g n ) O(nlog {\ }n) O(nlog n)复杂度的交顿操作,加起来总的时间复杂度为 O ( n l o g n ) O(nlog {\ }n) O(nlog n)
最好的情况则是正序的时候,只需要进行 O ( n l o g n ) O(nlog {\ }n) O(nlog n)复杂度的比较操作,而不需移动操作,不过总的时间复杂度还是 O ( n l o g n ) O(nlog {\ }n) O(nlog n)。也就是说,待排序数据的原始分布情况对堆排序的效率影响是比较小的。
另外,堆排序也是不稳定排序。

4. 算法代码

算法代码如下:
Pyhton

#堆排序
def heap_sort(array) :"""这里需要注意两点:(1)递归思想(2)列表切片"""length = len(array)#当数组 array 的长度为1时,说明只有一个元素if length<= 1:#无须排序,直接返回原列表return array# 若存在两个或以上节点else:#调整成大顶堆:按照先从下往上,再从左到右的顺序进行调整# 从最后一个非叶子节点(length//2-1)开始向前遍历,直到根节点for i in range(length//2-1,-1,-1):# //为取整# 当左孩儿大于父节点时if array[2*i+1] > array[i]:#二者交換位置array[2*i+1],array[i]=array[i],array[2*i+1] # 如果右孩儿存在且大于父节点时if 2*i+2 <= length-1:if array[2*i+2]> array[i]:#二者交換位置array[2*i+2],array[i] = array[i], array[2*i+2]'''此处省略重构建过程,对结果并不影响!'''# 将堆顶元素与末尾元素进行交换,使最大元素“沉”到数组末尾array[0],array[length-1] = array[length-1], array[0]#递归调用 heap_sort函数对前n-1个元素进行堆排序并返回排序后的结果return heap_sort(array [0:length-1]) + array[length-1:]
# 调用 heapsort 函数
print(heap_sort([34, 21, 13, 2, 5, 1, 55, 3, 1, 8]))

Java

    public static int[] heap_sort(int[] array) {int length = array.length;if (length <= 1) {return array;} else {// 构建最大堆for (int i = length / 2 - 1; i >= 0; i--) {maxHeapify(array, i, length);}// 交换堆顶元素与末尾元素并减小堆大小for (int end = length - 1; end > 0; end--) {swap(array, 0, end);length--;maxHeapify(array, 0, length); // 调整堆}}return array;}private static void maxHeapify(int[] array, int i, int size) {int left = 2 * i + 1;int right = 2 * i + 2;int largest = i;if (left < size && array[left] > array[largest]) {largest = left;}if (right < size && array[right] > array[largest]) {largest = right;}if (largest != i) {swap(array, i, largest);maxHeapify(array, largest, size);}}private static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;}@Testvoid contextLoads () {int[] array={34, 21, 13, 2, 5, 1, 55, 3, 1, 8};System.out.println(Arrays.toString(heap_sort(array)));}

5. 输出结果

在这里插入图片描述

6. 算法过程分解

第1次递归
待排序数组如下:
[34, 21, 13, 2, 5, 1, 55, 3, 1, 8]
第1次返回结果:[1, 1, 2, 3, 5, 8, 13, 21, 34]+[55]
第2次递归
待排序数组如下:
[5, 21, 34, 3, 8, 1, 13, 2, 1]
第2次返回结果:[1, 1, 2, 3, 5, 8, 13, 21]+[34]
第3次递归
待排序数组如下:
[1, 5, 21, 3, 8, 1, 13, 2]
第3次返回结果:[1, 1, 2, 3, 5, 8, 13]+[21]
第4次递归
待排序数组如下:
[2, 1, 8, 3, 5, 1, 13]
第4次返回结果:[1, 1, 2, 3, 5, 8]+[13]
第5次递归
待排序数组如下:
[8, 2, 5, 1, 3, 1]
第5次返回结果:[1, 1, 2, 3, 5]+[8]
第6次递归
待排序数组如下:
[1, 3, 5, 1, 2]
第6次返回结果:[1, 1, 2, 3]+[5]
第7次递归
待排序数组如下:
[2, 1, 3, 1]
第7次返回结果:[1, 1, 2]+[3]
第8次递归
待排序数组如下:
[1, 1, 2]
第8次返回结果:[1, 1]+[2]
第9次递归
待排序数组如下:
[1, 1]
第9次返回结果:[1]+[1]
第10次递归
待排序数组如下:
[1]
因为只有一个元素,所以无须排序
第10次返回结果:[1]

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

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

相关文章

【深度测试】看到技术方案后,该怎么进行分析和测试

测试左移的思想&#xff0c;讲究尽早测试&#xff0c;测试是一系列的行为&#xff0c;并不一定要等代码运行起来才能测&#xff0c;下面会分享一些经验&#xff0c;提供大家参考。 一、静态分析 1.1 分析方法调用链 目标&#xff1a;梳理结构&#xff0c;化繁为简 原理&#…

寒假 day2

1、请编程实现单向循环链表的头插&#xff0c;头删、尾插、尾删 #include<stdio.h> #include<string.h> #include<stdlib.h> enum{FALSE-1,SUCCESS}; typedef int datatype; //定义节点结构体 //节点&#xff1a;数据域、指针域 typedef struct Node {//数…

美国纳斯达克大屏怎么投放:投放完成需要多长时间-大舍传媒Dashe Media

陕西大舍广告传媒有限公司&#xff08;Shaanxi Dashe Advertising Media Co., Ltd&#xff09;&#xff0c;简称大舍传媒&#xff08;Dashe Media&#xff09;&#xff0c;是纳斯达克在中国区的总代理&#xff08;China General Agent&#xff09;。与纳斯达克合作已经有八年的…

Visual Studio 最新版安装教程

Visual Studio简介 Visual Studio是一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;广泛应用于.NET和C工作负载以及许多其他语言和框架的开发。它提供了一套完整的工具集&#xff0c;包括UML工具、代码管控工具、集成开发环境&#xff08;IDE&#xff09;等&#xff…

css1引入方式

一.行内样式表(行内式&#xff09;&#xff08;用在标签内部&#xff09;&#xff08;用于修改一些简单的&#xff09; 二.内部样式表&#xff08;嵌入式&#xff09;&#xff08;平常练习的呢种&#xff09; 三 .外部样式表&#xff08;链接式&#xff09; 写在一个单独的文件…

Hadoop3.x基础(3)- Yarn

来源&#xff1a;B站尚硅谷 目录 Yarn资源调度器Yarn基础架构Yarn工作机制作业提交全过程Yarn调度器和调度算法先进先出调度器&#xff08;FIFO&#xff09;容量调度器&#xff08;Capacity Scheduler&#xff09;公平调度器&#xff08;Fair Scheduler&#xff09; Yarn常用命…

ele-h5项目使用vue3+vite开发:第四节、业务组件-SearchView组件开发

需求分析 展示切换动画搜索框输入文字&#xff0c;自动发送请求搜索结果展示搜索状态维护历史搜索展示&#xff0c;点击历史搜索后发送请求历史搜索更多切换动画效果 <script setup lang"ts"> import OpSearch from /components/OpSearch.vue import { ref } f…

苹果CMS挖片网升级版视频主题模版源码

自适应视频站正版高级挖片网收录模板&#xff0c;模板不错&#xff0c;是挖片网的升级版。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/88799583 更多资源下载&#xff1a;关注我。

第十二篇【传奇开心果系列】Python的OpenCV技术点案例示例:视频流处理

传奇开心果短博文系列 系列短博文目录Python的OpenCV技术点案例示例短博文系列短博文目录一、前言二、视频流处理介绍三、实时视频流处理示例代码四、视频流分析示例代码五、归纳总结系列短博文目录 Python的OpenCV技术点案例示例短博文系列 短博文目录 一、前言 OpenCV视频…

UnityShader(十四)纹理

目录 前言&#xff1a; 单张纹理实现效果&#xff1a; 效果&#xff1a; 前言&#xff1a; 纹理最初的目的是用一张图片来控制模型的外观。使用纹理映射技术我们可以把一张图“贴”在模型表面&#xff0c;逐纹素&#xff08;文素的名字是为了和像素进行区分&#xff09;控制…

js数组和字符串之间的转换方式以及数组的一些方法

一、数组和字符串之间的转换方式 1&#xff09;将字符串切割成字符串数组—stringObject.split(separator, howmany) seperator-----字符串、正则表达式&#xff0c;必需 howmany------指定返回的数组的最大长度&#xff0c;可省略&#xff0c;省略后全量返回 源代码 var str&q…

VBA技术资料MF114:批量给Word文档添加页眉

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…