深入理解堆排序:建堆、排序与优化

news2025/1/22 23:00:58

引言

在计算机科学中,堆排序是一种高效的排序算法,利用堆的数据结构特性进行排序。本文将深入探讨堆排序的原理、实现过程,并介绍一种优化方法,以帮助读者更好地理解和运用这一经典算法

 

目录

  1. 堆排序简介

    • 1.1 什么是堆排序?

    • 1.2 堆排序的应用场景

  2. 建堆过程

    • 2.1 堆的基本概念回顾

    • 2.2 第一种建堆方法:向上调整

    • 2.3 代码实例:建立小堆的问题

  3. 排序过程

    • 3.1 选择建大堆的原因

    • 3.2 代码优化:建大堆方式

    • 3.3 排序实现详解

  4. 性能分析与优化

    • 4.1 时间复杂度分析

    • 4.2 为何选择建大堆?

    • 4.3 可能的性能优化方案

     5.总结与展望

  • 5.1 堆排序的优缺点总结

  • 5.2 未来的优化方向

 

1. 堆排序简介

1.1 什么是堆排序?

堆排序是一种基于堆数据结构的排序算法,具有稳定性和较高的性能。它分为建堆和排序两个阶段,通过利用堆的性质在O(n log n)时间内完成排序。

1.2 堆排序的应用场景 

堆排序在实际应用中广泛用于大数据集合的排序,以及需要动态维护最大(或最小)元素的场景,比如实时系统中的任务调度

 2. 建堆过程

2.1 堆的基本概念回顾

在开始深入堆排序之前,我们先回顾一下堆的基本概念,包括大堆和小堆的定义

堆是一种特殊的树形数据结构,它满足以下两个基本性质:

  1. 堆的完全二叉树性质: 堆是一棵完全二叉树,即除了最后一层,其他层都是满的,并且最后一层的节点尽量靠左排列。

  2. 堆序性质: 对于每个节点i,父节点的值总是大于等于(大堆)或小于等于(小堆)其子节点的值。

堆可以分为两种类型:大堆和小堆。它们的区别在于堆序性质的不同。

  • 大堆(Max Heap): 在大堆中,每个节点的值都大于等于其子节点的值。根节点是堆中的最大值。

  • 小堆(Min Heap): 在小堆中,每个节点的值都小于等于其子节点的值。根节点是堆中的最小值。

通过这种特殊的堆结构,堆排序能够高效地进行升序(小堆)或降序(大堆)排序。

理解了堆的基本概念后,我们将深入探讨堆排序的建堆过程、排序过程以及可能的性能优化。让我们继续阐述堆排序的实现和优化方法。

2.2 第一种建堆方法:向上调整

以下是第一种方法 我们采用向上调整  先进行建堆

void HeapSort(int* a, int n)
{
	//第一种 向上调整 小堆
	for (int i = 1; i < n; i++)
	{
		Adjustup(a, i);
	}//建堆


}

 接下来填写我们的测试用例

int main()
{
	int a[] = { 4,6,7,2,8,4,9 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d",a[i]);
	}
	return 0;
}

2.3 代码实例:建立小堆的问题 

 要理解堆排序不麻烦

堆排序的特点是可以帮助我们选树  是一种选择排序

但有个问题 我们现在要排的是升序  建的是大堆还是小堆呢

结论:大堆

先看看我们先前排序的逻辑结构

 物理上是一个数组

 如果我们说升序选择是建小堆,那我们就是要选择出最小数 那么我们如何选出次小的树呢

也就是说我们第一个数是最小的不用动 剩下的重新建堆

 剩下的树有可能是堆,也有可能不是堆

为什么这么说呢  因为他们之间的关系已经全部被打乱 原先的结构改变了

只能重新建堆   那么代价就有些大 复杂度  建堆时间复杂度是  N*logN

所以他的时间复杂度就是N^2 * logN

所以建小堆进行排序是不可取的 

 所以

我们采用建大堆的方法

3. 排序过程

3.1 选择建大堆的原因

在进行堆排序时,我们选择建立大堆而不是小堆的主要原因在于效率。建立小堆可能会导致在排序的过程中需要不断地进行调整,增加了时间复杂度。相比之下,建立大堆的方式更为高效,可以降低整体的时间复杂度。

3.1.1 大堆的优势

  • 减少调整次数: 建立大堆时,较大的元素会逐渐上浮到堆顶,不容易被后续较小的元素打乱。因此,后续的调整次数相对较少,提高了效率。

  • 避免元素关系破坏: 在建立小堆的过程中,元素之间的关系可能被打乱,导致需要额外的调整操作。而建立大堆时,元素关系的破坏相对较小,减少了重新调整的需求。

 

3.2 代码优化:建大堆方式

为了改进建堆的方式,我们对原有的建堆代码进行轻微的修改,以建立大堆。

我们先轻微改动一下代码

 

 

 

3.2.1 优化的核心思想

  • 自底向上调整: 从堆的中间位置开始向根节点遍历,对每个节点进行向下调整。这样可以确保在调整的过程中,较大的元素逐渐下沉到堆的底部。

  • 适应大堆排序: 建立大堆后,排序过程将更加高效,不需要频繁调整元素位置。

3.3 排序实现详解

排序的实现主要分为两个步骤:建堆和排序。

3.3.1 建大堆

在建立大堆的过程中,我们将数组转化为一个有效的大堆结构。

3.3.2 排序

排序阶段利用了堆的性质,每次将堆顶元素(最大元素)与堆的最后一个元素交换,然后对剩余的堆进行调整,确保剩余部分仍然是一个大堆。重复这个过程,直到整个数组有序。

 

 此时我们建成了一个大堆

我们让其首尾交换 并不将最后一个数看作堆里的内容

 此时左右各是一个大堆 将树进行向下调整 选出次大的数是8  再将8与倒数第二个进行交换 再进行向下调整

 

 

 最后时间复杂度就是  n*logN 效率十分高

 

4. 性能分析与优化

4.1 时间复杂度分析

堆排序的时间复杂度主要分为两个部分:建堆的时间复杂度和排序的时间复杂度。

4.1.1 建堆的时间复杂度

在建堆阶段,我们采用了自底向上的方式建立大堆,其时间复杂度为 O(n*logN),其中 n 是数组的大小。这是因为我们只需对堆的一半元素进行向下调整,而堆的一半元素之后都是叶子节点,不需要调整。

4.1.2 排序的时间复杂度

在排序阶段,每次将堆顶元素与最后一个元素交换,并调整堆。因为堆的高度是logn,所以每次调整的时间复杂度是logn,总的排序时间复杂度为 O(nlogn)。

综合考虑建堆和排序阶段,堆排序的总体时间复杂度为 O(n+nlogn)=O(nlogn)。

4.2 选择建大堆的原因

选择建大堆的主要原因在于优化排序过程。通过建大堆,我们能够减少调整的次数,提高整体的排序效率。

4.2.1 优势总结
  • 较少调整次数: 大堆的建立方式使得较大的元素逐渐上浮到堆顶,减少了后续调整的次数。

  • 避免元素关系破坏: 相较于建小堆,建大堆时元素关系的破坏相对较小,降低了重新调整的需求。

4.3 优化方案与实践

在实现中,通过调整建堆的方式,我们采用了自底向上的方法建立大堆。这一优化方案在实践中表现出色,有效减少了整体的时间复杂度。

4.3.1 自底向上的建堆

通过遍历堆的一半元素,从底部向上进行调整,我们有效地建立了一个大堆。这一方法的实际效果在于减少了调整的次数,提高了建堆的效率。

4.3.2 选择建大堆的实践

在排序过程中,我们通过选择建立大堆的方式,使得排序阶段的调整次数减少,大大提高了排序的效率。这一实践得以验证,使得堆排序在实际应用中具有更好的性能表现。

在接下来的部分,我们将设计测试用例并分析实验结果,以验证堆排序的正确性和性能。

在堆排序中,建堆阶段通常使用向下调整(也叫做下沉、下调)的方式。这是因为向下调整相对于向上调整更为高效。

让我们来看一下向下调整的主要优势:

  1. 高效性: 向下调整可以在一次遍历中将一个无序的数组转化为堆结构。相比之下,向上调整可能需要多次遍历来达到相同的效果。

  2. 自底向上: 向下调整是自底向上的过程,从数组的中间位置开始,直至叶子节点。这种自底向上的方式使得调整的效率更高,因为在数组中较小的元素会逐渐沉到底部。

  3. 实现简单: 向下调整的实现相对简单直观,通常需要比向上调整更少的代码。

尽管向上调整也可以用于建堆,但通常情况下,向下调整被认为更为经典和高效。因此,在堆排序中,建堆阶段一般选择向下调整的方式,而排序阶段使用向上调整。这种组合使得整个堆排序算法在时间复杂度和实现复杂度上都能达到较好的平衡。

 

void HeapSort(int* a, int n)
{
	//第一种 向上调整 大堆
	//for (int i = 1; i < n; i++)
	//{
	//	Adjustup(a,i);
	//}//建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		Adjustdown(a, n, i);
	}
	int end = n - 1;
	while (end>0)
	{
		Swap(&a[0], &a[end]);
		Adjustdown(a, end, 0);
		--end;
	}

}
int main()
{
	int a[] = { 4,6,7,2,8,4,9 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d",a[i]);
	}
	return 0;
}

 

5.2 未来的优化方向

尽管堆排序已经在很多方面取得了较好的性能,但在一些特定场景下,仍然存在一些可以进一步优化的空间。

5.2.1 多线程优化

在大规模数据集的排序中,考虑通过多线程并行处理来提高排序的速度,充分利用现代计算机的多核架构。

5.2.2 内存局部性优化

对于大规模数据集,可以考虑优化算法以提高内存局部性,减少缓存未命中,从而进一步提高排序性能。

5.2.3 适用性扩展

尝试将堆排序与其他排序算法结合,形成一种更为适用于多样化数据特征的混合排序策略,以提高算法的适用性。

综上所述,堆排序作为一种高效的排序算法,在实际应用中仍具有重要地位。随着计算机硬件和算法优化的不断发展,堆排序可能在更多场景中发挥其优势。未来的研究方向包括算法并行化、内存局部性优化等方面的探索,以进一步提高堆排序的性能。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【精选】VulnHub red 超详细过程思路

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

k8s-daemonset、job、cronjob控制器 6

Daemonset控制器&#xff08;一个节点部署一个&#xff09; 、 创建Daemonset控制器 控制节点上不能进行部署&#xff0c;有污点 解决方式&#xff1a; 扩容节点&#xff0c;token值过期的解决方法&#xff1a; 回收pod job控制器 需要使用perl镜像&#xff0c;仓库没有&…

PyQt基础_009_ 按钮类控件QSlider

基本功能 import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class SliderDemo(QWidget):def __init__(self, parentNone):super(SliderDemo, self).__init__(parent)self.setWindowTitle("QSlider 例子") self.resize…

优雅测试代码:使用Go实现高效单元测试!

简介 日常开发中, 测试是不能缺少的. Go 标准库中有一个叫做 testing 的测试框架, 可以用于单元测试和性能测试. 它是和命令 go test 集成使用的. 测试文件是以后缀 _test.go 命名的, 通常和被测试的文件放在同一个包中. 单元测试 单元测试的格式形如: func TestAbs(t *t…

Stable Diffusion绘画系列【6】:东方美学作品

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

速记:一个保险丝检测电路

一个保险丝检测电路 保险丝熔断&#xff1a;红灯亮 保险丝正常&#xff1a;绿灯亮 同样的&#xff0c;仿真中的指示灯可以换成其他指示器件。

谨慎Apache-Zookeeper-3.5.5以后在CentOS7.X安装的坑

目录 前言 一、现场还原 二、问题诊断 三、问题原因 总结 前言 最近由于项目需要&#xff0c;在服务器上需要搭建Hbase完全分布式集群环境。开发环境&#xff0c;采用的是最小节点的方式进行搭建&#xff08;即3个节点的模式&#xff09;。资源环境列表如下&#xff1a; 序号…

go学习之文件操作与命令行参数

文章目录 一、文件操作1.基本介绍2.常用文件操作函数和方法3.关于文件操作应用实例4.写文件操作应用实例&#xff08;创建文件并写入文件&#xff09;1&#xff09;基本介绍2&#xff09;基本应用实例-方式一 5.判断文件是否存在6.统计英文、数字、空格和其他字符数量 二、命令…

Bert-VITS2本地部署遇到的错误

关于Bert-VITS2本地部署遇到的错误 1、在下载python中相关依赖时报错 building ‘hdbscan._hdbscan_tree’ extension error: Microsoft Visual C 14.0 or greater is required. Get it with “Microsoft C Build Tools”: https://visualstudio.microsoft.com/visual-cpp-bu…

如何在安防视频监控平台EasyCVR首页增添统计设备每个小时的温度展示功能?细节如下

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

人工智能对人脑的探索研究!物理限制推动类脑人工智能的发展

原创 | 文 BFT机器人 在一项开创性的研究中&#xff0c;剑桥科学家采用了一种新颖的人工智能方法&#xff0c;展示了物理约束如何深刻影响了人工智能系统的发展。 这项研究会让人想起人脑的发育和能力限制&#xff0c;为复杂神经系统的进化提供了新的见解。通过整合这些限制&a…

基于YOLOv5的人群计数系统设计系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介系统概述系统功能核心技术系统架构系统优势 二、功能三、系统四. 总结  总结 一项目简介 基于YOLOv5的人群计数系统设计是一个非常有趣且具有挑战性的项目…

输入通道数 和 输出通道数 的理解

输入通道数&#xff08;in_channels&#xff09;输出通道数&#xff08;out_channels&#xff09; 在卷积神经网络中通常需要输入 in_channels 和 out_channels &#xff0c;即输入通道数和输出通道数&#xff0c;它们代表什么意思呢&#xff1f; 输入通道数&#xff08;in_c…

前端笔试遇到的坑-100题

1.闭包 let 形成闭包 var全局变量 function test() {for (var i 0; i < 6; i) {console.log(i); //1 2 3 4 5 6// setTimeout(() > {// console.log(i);// }, 0); 6 6 6 6 6 6 6} } test();var array []; for (var i 0; i < 3; i) {array.push(() > i);…

文件重命名:如何删除文件名中的下划线,特殊符号批量删除

在日常的工作中&#xff0c;经常会遇到文件名中包含特殊符号的情况&#xff0c;例如&#xff0c;一些文件名可能包含下划线、空格或其他特殊符号&#xff0c;这些符号可能会干扰我们的文件搜索和识别。此外&#xff0c;一些文件名可能包含无法识别的非标准字符&#xff0c;这可…

函数的极值与最值

函数的最值 1.闭区间上连续函数的最值 1.求驻点或不可导点&#xff08;可能的极值点&#xff09; 2.求函数在驻点&#xff0c;不可导点&#xff0c;端点的函数值 3.比较大小 例题&#xff1a; 例题思想&#xff1a;分段函数分段点必须验证导数的存在性 几种常见的最值类型 1.…

谱方法学习笔记-下(超详细)

谱方法学习笔记&#x1f4d2; 谱方法学习笔记-上(超详细) 声明&#xff1a;鉴于CSDN使用 K a T e X KaTeX KaTeX 渲染公式&#xff0c; KaTeX \KaTeX KATE​X 与 L a T e X LaTeX LaTeX 不同&#xff0c;不支持直接的交叉引用命令&#xff0c;如\label和\eqref。 KaTeX \KaT…

python监测GPU使用

参考&#xff1a; https://stackoverflow.com/questions/67707828/how-to-get-every-seconds-gpu-usage-in-python 自己测试 import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim import numpy as np import matplotlib.pyplot…

【从入门到起飞】JavaSE—多线程(3)(线程池,最大并行数)

&#x1f38a;专栏【JavaSE】 &#x1f354;喜欢的诗句&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f33a;以前写多线程的弊端&#x1f384;线程池的主…

NoSql非关系型数据库

前言&#xff1a;Nosql not only sql&#xff0c;意即“不仅仅是sql”&#xff0c;泛指非关系型数据库。这些类型的数据存储不需要固定的模式&#xff08;当然也有固定的模式&#xff09;&#xff0c;无需多余的操作就可以横向扩展。NoSql数据库中的数据是使用聚合模型来进行处…