堆的应用1——堆排序

news2024/12/23 10:39:14

一,堆排序

堆排序是一种基于比较的排序算法,它利用堆这种数据结构所设计。

堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父结点。

堆排序可以分为两个主要步骤:建堆和用堆删除思想调整排序。

1.堆排序实现思路

  1. 建堆
    • 初始时,将待排序序列构造成一个大顶堆(或小顶堆)。此时,整个序列的最大值(或最小值)就是堆顶的根结点。
    • 通常从最后一个非叶子结点开始调整,将其调整为一个堆,然后向前依次调整每一个非叶子结点,直到整个序列都成为一个堆。
  2. 堆调整排序
    • 此时整个序列的最大值(或最小值)就是堆顶的根结点。将其与末尾结点进行交换,此时末尾就为最大值。
    • 然后将剩余n-1个序列重新构造成一个堆,这样会得到n个元素中的次大值。如此反复执行,便能得到一个有序序列。

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

2.建堆

那我们到底怎么实现呢?

2.1向上调整建堆

 很简单我们以建大堆为例

// 以下部分是向上调整建堆的代码,但已经被注释掉,因为通常使用向下调整建堆,效率更高   
    for (int i = 1; i < n; ++i)  
    {  
        // 对第i个元素执行向上调整操作,使其满足堆的性质  
        // 这种方式建堆的时间复杂度为O(N*logN),不是最优的  
        AdjustUp(a, i);  
    }  
    

2.2向上调整建堆和向下调整建堆时间复杂度 

//向上调整建堆——O(N*logN)
for(int i=1;i<n;++i)
{
AdjustUp(a,i);
}

//向下调整建堆——O(N)
for(int i=(n-1-1)/2;i>=0;--i)
{
AdjustDown(a,n,i);
}

2.3向下调整建堆 

但是我们很快就会发现,这虽然可以,但是下面的堆调整排序用的是向下调整,我们这里如果使用向上调整,它的时间复杂度比向下调整的大,这会加大消耗,所以我们使用向下调整 

// 建堆 -- 向下调整建堆 -- O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

2.4建什么堆 

我们向排升序,我们建什么堆?降序呢?

我们以升序为例

有人说升序建小堆,因为打印小堆的第一个元素最小嘛但是这样子看起来轻松,但是实际上真的轻松吗?

我们要选次小的,只能将除了根节点之外的其他元素视作一个堆,这样子后面的关系全乱了——原来的兄弟变父子了,父子变兄弟了,那就重新建堆了,时间复杂度O(N*logN),代价太大了

不如直接遍历选择O(logN)

所以我们升序建大堆,降序建小堆

3 .堆调整排序

  • 此时整个序列的最大值(或最小值)就是堆顶的根结点。将其与末尾结点进行交换,此时末尾就为最大值(最小值)。
  • 然后将剩余n-1个序列重新构造成一个堆,这样会得到n个元素中的次大(小)值。如此反复执行,便能得到一个有序序列。

简单的来说就是建堆得最大(小)值然后放到最后面去,对剩下的(除了放到后面去的)元素建堆选出最大(小)值,再放到后面去,再建堆,再放到后面去,依次类推 

假设我们已经建好了一个大堆

int a[]={20,17,4,16,5,3};

则堆排序应该是这样子的 

代码实现

int end = n - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);

		--end;
	}

4.堆排序代码实现

// HeapSort函数:使用堆排序算法对数组a进行升序排序  
void HeapSort(int* a, int n)  
{   

    // 使用向下调整建堆,从最后一个非叶子节点开始向前遍历  
    for (int i = (n - 1 - 1) / 2; i >= 0; --i)  
    {  
        // 对当前非叶子节点执行向下调整操作,使其及其子树满足堆的性质  
        // 由于每个非叶子节点只调整一次,因此总的时间复杂度为O(N)  
        AdjustDown(a, n, i);  
    }  
  
    // 以下部分是堆排序的主体,将堆顶的最大值(大顶堆)与当前未排序部分的最后一个元素交换  
    // 并重新调整堆,以保持堆的性质,直到整个数组排序完成  
    int end = n - 1; // end指向未排序部分的最后一个元素的索引  
    while (end > 0)  
    {  
        // 将堆顶元素(当前最大值)与未排序部分的最后一个元素交换  
        Swap(&a[end], &a[0]);  
  
        // 重新调整堆,保持堆的性质,此时堆的大小减少了1(因为已经取出了一个元素)  
        AdjustDown(a, end, 0);  
  
        // 缩小未排序部分的范围  
        --end;  
    }  
}

HeapSort 函数是用于执行堆排序的主要函数,它包括建堆和堆排序的两个主要步骤。

这里您选择使用向下调整的方法来建堆,这是一个高效的方法,因为向下调整每个非叶子结点只需要O(logN)的时间,并且整个建堆过程的时间复杂度为O(N)。所以整个堆排序过程的时间复杂度为O(N*logN)。 

5.代码解释

建堆 -- 向下调整建堆 -- O(N)

 for (int i = (n - 1 - 1) / 2; i >= 0; --i)  
 {  
 AdjustDown(a, n, i);  
 } 

这段代码从最后一个非叶子结点开始,向前遍历到根结点,并对每个结点调用AdjustDown函数进行向下调整。

由于非叶子结点的索引是(n - 1) / 2(向下取整),这里(n - 1 - 1) / 2是为了得到正确的最后一个非叶子结点的索引。每个结点的向下调整时间复杂度为O(logN),但由于每个结点只调整一次,所以整个建堆过程的时间复杂度为O(N)。

堆排序 -- O(N*logN)

 int end = n - 1;  
 while (end > 0)  
 {  
 Swap(&a[end], &a[0]);  
 AdjustDown(a, end, 0);  
 --end;  
 } 

这部分代码执行堆排序的主体部分。

它首先将堆顶元素(即当前最大值)与数组的末尾元素交换,然后调整剩下的元素使其恢复为大顶堆的性质。这个过程反复进行,直到堆中只剩下一个元素为止。每次交换和调整的时间复杂度为O(logN),因为需要调整n-1次,所以整个堆排序过程的时间复杂度为O(N*logN)。 

6.测试代码

#include<stdio.h>
typedef int HPDataType;

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType x = *p1;
	*p1 = *p2;
	*p2 = x;
}

//堆的向下调整(大堆)  
void AdjustDown(HPDataType* a, int n, int parent)
{
    int child = parent * 2 + 1; // 计算左子节点的索引  

    // 当 child 索引在数组范围内时执行循环  
    while (child < n)
    {
        // 如果右子节点存在且大于左子节点  
        if (child + 1 < n && a[child + 1] > a[child])
        {
            ++child; // 更新 child 为右子节点的索引  
        }

        // 如果 child 节点(现在是左右子节点中较大的一个)大于 parent 节点  
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]); // 交换 parent 和 child 的值  
            parent = child; // 更新 parent 为刚刚交换过的 child 的索引  
            child = parent * 2 + 1; // 重新计算左子节点的索引  
        }
        else
        {
            break; // child 节点不大于 parent 节点,无需继续调整,退出循环  
        }
    }
}


// HeapSort函数:使用堆排序算法对数组a进行升序排序  
void HeapSort(int* a, int n)
{

    // 使用向下调整建堆,从最后一个非叶子节点开始向前遍历  
    for (int i = (n - 1 - 1) / 2; i >= 0; --i)
    {
        // 对当前非叶子节点执行向下调整操作,使其及其子树满足堆的性质  
        // 由于每个非叶子节点只调整一次,因此总的时间复杂度为O(N)  
        AdjustDown(a, n, i);
    }

    // 以下部分是堆排序的主体,将堆顶的最大值(大顶堆)与当前未排序部分的最后一个元素交换  
    // 并重新调整堆,以保持堆的性质,直到整个数组排序完成  
    int end = n - 1; // end指向未排序部分的最后一个元素的索引  
    while (end > 0)
    {
        // 将堆顶元素(当前最大值)与未排序部分的最后一个元素交换  
        Swap(&a[end], &a[0]);

        // 重新调整堆,保持堆的性质,此时堆的大小减少了1(因为已经取出了一个元素)  
        AdjustDown(a, end, 0);

        // 缩小未排序部分的范围  
        --end;
    }
}
int main()
{
	int a[] = { 2,6,8,9,4,5,1,3,7 };
	int size = sizeof(a) / sizeof(a[0]);
	HeapSort(a, size);
	for (int i = 0; i < size; i++)
	{
		printf("%d ", a[i]);
	}
}

 

大家一定要注意:升序建大堆,降序建小堆

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

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

相关文章

【python】模拟巴特沃斯滤波器

巴特沃斯滤波器&#xff08;Butterworth Filter&#xff09;&#xff0c;以其设计者斯蒂芬巴特沃斯&#xff08;Stephen Butterworth&#xff09;的名字命名&#xff0c;是一种具有平滑频率响应的滤波器。这种滤波器在频域中具有非常平坦的无波纹响应&#xff0c;直到它达到截止…

Linux(openEuler、CentOS8)企业内网DHCP服务器搭建(固定Mac获取指定IP)

----本实验环境为openEuler系统<以server方式安装>&#xff08;CentOS8基本一致&#xff0c;可参考本文&#xff09;---- 目录 一、知识点二、实验&#xff08;一&#xff09;为服务器配置网卡和IP&#xff08;二&#xff09;为服务器安装DHCP服务软件&#xff08;三&a…

iOS 17 / iPad OS 17屏蔽更新

iOS 17 / iPad OS 17屏蔽更新 1&#xff0c;进入屏蔽iOS更新的描述文件下载链接 下载链接 wx 搜索 Geek 前端发送屏蔽更新进行获取 2&#xff0c;复制这段链接&#xff0c;在Safari浏览器中打开&#xff0c;注意打开后别点击下载&#xff01;要先改时间&#xff01; 3&#…

实例分割——Mask R-CNN、YOLOV8、RTMDET、DeepLab四种实例分割算法比对

1.概述 1.1 语义分割与实例分割 实例分割和语义分割都是计算机视觉领域中图像分割的任务&#xff0c;它们在目标和方法上有一些区别&#xff1a; 语义分割&#xff1a; 语义分割的目标是对图像中的每个像素打上类别标签&#xff0c;即识别出图像中每个像素属于哪个预定义的…

Redis简介和数据结构

目录 简介 进入之后身份认证才能使用 优点 用途&#xff1a; 数据结构 string string自动扩容 Redis中的简单动态字符串&#xff08;SDS&#xff09;具有以下优点&#xff1a; SDS数据的编码格式 比较&#xff1a; string 常用操作 分布式锁 使用情况&#xff0c;…

算法day03

第一题 179. 查找总价格为目标值的两个商品 本题采用前后指针和单调性规律可解&#xff1b; 解题思路如下&#xff1a; 1、设置前后指针 2、前后指针所指的两数之和大于目标数&#xff0c;右指针左移&#xff1b; 前后指针所指的两数之和小于目标数&#xff0c;左指针右移&…

3D 打印为压铸行业的带来新动力

近年来&#xff0c;随着多家车企的积极引领&#xff0c;一体化压铸技术已逐渐成为汽车行业的一大趋势。该技术不仅简化了车身的制造流程&#xff0c;而且优化了供应链环节&#xff0c;成为汽车制造业中的一次创新&#xff0c;同时显著提升了经济效益。 压铸技术&#xff0c;简而…

excel中怎么跳转到指定的单元格?

也许你会有这样的需求&#xff0c;如A1单元格中显示B100这种单元格地址&#xff0c;怎么做以点一下就跳转到B100&#xff1f; 一、设置公式 B1HYPERLINK("#"&MID(CELL("FILENAME",A1),FIND("]",CELL("FILENAME",A1))1,99)&&…

echart 多表联动value为null时 tooltip 显示问题

两个图表&#xff0c;第一个有tooltip,第二个隐藏掉 两个图表的 series 如下 // ----- chart1 ----series: [{name: Union Ads,type: line,stack: Total,data: [320, 282, 391, 334, null, null, null],},{name: Email,type: line,stack: Total,data: [220, 232, 221, 234, 29…

现货黄金流程到何种程度?现货黄金在金融产品中的占比是多少?

踏入2024年以来&#xff0c;受美联储降息以及地缘局势紧张的影响&#xff0c;美元受压&#xff0c;避险情绪高涨&#xff0c;众多因素影响下黄金价格出现了强势的上涨&#xff0c;屡创历史新高。在上涨如此强劲的背景下&#xff0c;投资者希望通过黄金投资来实现资产增值。市场…

锁相环(PLL)经典书籍分享(可下载)

锁相环&#xff08;PLL&#xff09;作为一种确保系统同步的控制机制&#xff0c;广泛应用于频率合成、时钟恢复、信号调制与解调等多个方面。 探索锁相环&#xff08;PLL&#xff09;技术的深度和广度&#xff0c;最有效的途径之一便是通过专业书籍的系统学习。以下是为渴望掌…

每日OJ题_贪心算法三⑦_力扣991. 坏了的计算器

目录 力扣991. 坏了的计算器 解析代码 力扣991. 坏了的计算器 991. 坏了的计算器 难度 中等 在显示着数字 startValue 的坏计算器上&#xff0c;我们可以执行以下两种操作&#xff1a; 双倍&#xff08;Double&#xff09;&#xff1a;将显示屏上的数字乘 2&#xff1b;递…

英伟达推出视觉语言模型:VILA

NVIDIA和MIT的研究人员推出了一种新的视觉语言模型(VLM)预训练框架&#xff0c;名为VILA。这个框架旨在通过有效的嵌入对齐和动态神经网络架构&#xff0c;改进语言模型的视觉和文本的学习能力。VILA通过在大规模数据集如Coy0-700m上进行预训练&#xff0c;采用基于LLaVA模型的…

Core Impact 21.5 (Windows) - 高级渗透测试

Core Impact 21.5 (Windows) - 高级渗透测试 Fortra | Core Security Penetration testing software, Release Feb 2024 请访问原文链接&#xff1a;Core Impact 21.5 (Windows) - 高级渗透测试&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&…

多线程学习Day07

共享模型之不可变 从一个日期转换的问题开始 Slf4j(topic "c.Test1") public class Test1 {public static void main(String[] args) {SimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd");for (int i 0; i < 10; i) {new Thread(() -> {…

20K薪资要什么水平?来看看25岁测试工程师的面试过程…_测试工程师薪资20k(2)

既有适合小白学习的零基础资料&#xff0c;也有适合3年以上经验的小伙伴深入学习提升的进阶课程&#xff0c;涵盖了95%以上软件测试知识点&#xff0c;真正体系化&#xff01; 由于文件比较多&#xff0c;这里只是将部分目录截图出来&#xff0c;全套包含大厂面经、学习笔记、…

无监督式学习

1.是什么&#xff1f; 无监督式学习与监督式学习**最大的区别就是&#xff1a;**没有事先给定的训练实例&#xff0c;它是自动对输入的示例进行分类或者分群&#xff1b; 优点&#xff1a;不需要标签数据&#xff0c;极大程度上扩大了我们的数据样本&#xff0c;其次不受监督信…

华为云CodeArts API专场直播来袭!——探索API全生命周期管理新趋势

API的全生命周期管理是否让你摸不清头脑&#xff1f;你是否对API的前沿技术和应用充满了好奇&#xff0c;渴望一探究竟&#xff1f; 华为云PaaS服务即将在5月10日16:00&#xff0c;为你带来一场别开生面的CodeArts API专场直播活动&#xff01; 你可以在轻松愉快的氛围中&…

Python网络编程 03 实验:FTP详解

文章目录 一、小实验FTP程序需求二、项目文件架构三、服务端1、conf/settings.py2、conf/accounts.cgf3、conf/STATUS_CODE.py4、启动文件 bin/ftp_server.py5、core/main.py6、core/server.py 四、客户端1、conf/STATUS_CODE.py2、bin/ftp_client.py 五、在终端操作示例 一、小…

【CTF-Crypto】RSA-选择明密文攻击 一文通

RSA&#xff1a;选择明密文攻击 关于选择明/密文攻击&#xff0c;其实这一般是打一套组合拳的&#xff0c;在网上找到了利用的思路&#xff0c;感觉下面这个题目是真正将这个问题实现了&#xff0c;所以还是非常棒的一道题&#xff0c;下面先了解一下该知识点&#xff1a;(来自…