堆的应用1——堆排序

一,堆排序

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

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

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

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.hqwc.cn/news/682043.html

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

相关文章

52. 【Android教程】网页视图:WebView

在前面的章节我们所围绕的全部都是纯客户端开发&#xff0c;我们叫 Native 开发。这样的好处就是体验和性能会非常好&#xff0c;但是在实际的使用中我们会发现存在大量的 H5 页面。这样就可以结合 Native / H5 双端的优势完成一个混合开发&#xff0c;而在这种开发模式中首当其…

[华为OD] B卷 树状结构查询 200

题目&#xff1a; 通常使用多行的节点、父节点表示一棵树&#xff0c;比如 西安 陕西 陕西 中国 江西 中国 中国 亚洲 泰国 亚洲 输入一个节点之后&#xff0c;请打印出来树中他的所有下层节点 输入描述 第一行输入行数&#xff0c;下面是多行数据&#xff0c;每行以空…

Sarcasm detection论文解析 |基于语义知识和辅助信息增强讽刺检测方法

论文地址 论文地址&#xff1a;https://www.sciencedirect.com/science/article/abs/pii/S0306457322000139?via%3Dihub 论文首页 笔记框架 基于语义知识和辅助信息增强讽刺检测方法 &#x1f4c5;出版年份:2022 &#x1f4d6;出版期刊:Information Processing & Managem…

【独家】武汉市中级职称申报基本条件大揭秘,你符合吗?

【独家】武汉市中级职称申报基本条件大揭秘&#xff0c;你符合吗&#xff1f; 2024年申报武汉市的中级职称条件欠缺看看周边地级市的 2024年4月底在武汉人事考试院发布了关于“武汉市若干专业技术职务水平能力测试报名工作的通知”。武汉市建筑类职称水平能力测试要开始了。 武…

Windows平台PyCharm之PySide6开发环境搭建与配置

PySide6 是一个用于创建跨平台 GUI 应用程序的库&#xff0c;它是 Qt for Python 的官方库。Qt 是一个跨平台的 C 应用程序框架&#xff0c;用于开发具有图形用户界面&#xff08;GUI&#xff09;的应用程序。PySide6 允许开发者使用 Python 语言访问 Qt 的功能&#xff0c;从而…

8、基本数据类型转换(自动转换和强制转换)

基本类型转换 1、自动类型转换2、强制类型转换 1、自动类型转换 定义&#xff1a;当Java程序在进行赋值或者运算时&#xff0c;精度小的类型会自动转换成精度大的数据类型&#xff0c;这个就是自动类型转换。&#xff08;自动小转大&#xff09; 背多芬&#xff1a; 这里要明…

探讨关于AutoPSA里CII算法的结构荷载

UKP3D,AutoPDMS导出应力计算文件至管道应力分析软件分析&#xff0c;如下图AutoPSA.用户咨询如图 1.如果计算时考虑水重&#xff0c;把工况中的w改为ww&#xff1b; 2.CAD表格中结构荷载不是单纯的1.5倍&#xff0c;是参照仿GLIF的算法&#xff0c;计算了水重的&#xff08;根…

Abp框架,EF 生成迁移文件时,自动添加表和字段注释内容

在使用 abp 框架&#xff0c;或者ef 的时候都会遇到一个问题&#xff0c;就是建实体后要将实体描述生成到数据库中&#xff0c;就需要手动去添加 [Comment("注释内容")] 注解&#xff0c;这样相当于手动写两次注释&#xff08;即使你是 Ctrl C&#xff09;&#x…

Apipost使用心得,让接口文档变得更清晰,更快捷

Idea和Apipost结合使用 Idea 安装插件Apipost-Helper-2.0 在【file】–>【settings】–>【Plugins】搜索 “Apipost-Helper-2.0”–>【install】&#xff0c;重启Idea 编写controller接口 在idea中编写业务功能及接口之后&#xff0c;在controller中鼠标【右键】单…

分享10个高质量宝藏网站~

分享一波高质量宝藏网站~ 这10个宝藏网站&#xff0c;个个都好用到爆&#xff0c;娱乐、办公、学习都能在这里找到&#xff01; 1、Z-Library https://zh.zlibrary-be.se/ 世界最大的免费电子书下载网站&#xff01;电子书资源超千万&#xff0c;不过这个网站不太稳定&#…

复现NerfingMVS(更新中)

按以下代码一步步操作 conda create -n NerfingMVS python3.7 conda activate NerfingMVS conda install pytorch1.7.1 torchvision0.8.2 torchaudio0.7.2 -c pytorch pip install -r requirements.txthttps://colmap.github.io/install.html Linux 中 建议的依赖&#xff1…

机器学习:基于线性回归、岭回归、xgboost回归、Lasso回归、随机森林回归预测卡路里消耗

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…