堆的应用(堆排序、Top-K问题)

文章目录

  • 1 堆排序
  • 2 Top-K问题

1 堆排序

堆排序是一种基于二叉堆(通常使用数组实现)的排序算法。
它的基本思想是利用堆这种数据结构的性质,通过建立一个堆(大堆或小堆),使得堆的根节点是所有节点中的最大值(大堆)或最小值(小堆)。然后,将根节点与堆的最后一个节点交换,使得最大值或最小值进入有序区。接着,对剩余的未排序部分重新调整成堆,重复这个过程,直到整个数组有序。

建堆和堆调整堆中都用到了向下调整,因此掌握了向下调整法,就可以完成堆排序。对该算法不清楚的,可以参考这篇文章,里面进行了详细的介绍:堆详解(C语言实现)
堆排序步骤:

  1. 构建初始堆(建堆): 从最后一个非叶子节点开始,对每个节点进行向下调整(调整成堆的性质,大堆或小堆)。
    排升序:建大堆,原因如下:
    在堆排序中,升序排序要建立大堆的主要原因是为了保证每次选择堆顶元素都是堆中的最大值
    在排升序时,每次选择堆顶元素与堆的最后一个元素交换,由于堆顶是最大值,将其与末尾元素交换后,最大值就被移到了数组的末尾,而在交换后,需要重新调整堆,使剩余部分重新构成大堆,这样,下一次选择堆顶元素时,依然得到的是剩余元素中的最大值。通过这个过程,每次都能选择到当前堆中的最大值,将其移到数组末尾,逐步形成有序部分,从而实现升序排序。
    排降序:建小堆,原因如下:
    在堆排序中,降序排序要建立小堆的主要原因是为了保证每次选择堆顶元素都是堆中的最小值
    在排降序时,每次选择堆顶元素与堆的最后一个元素交换,由于堆顶是最小值,将其与末尾元素交换后,最小值就被移到了数组的末尾,而在交换后,需要重新调整堆,使剩余部分重新构成小堆,这样,下一次选择堆顶元素时,依然得到的是剩余元素中的最小值。通过这个过程,每次都能选择到当前堆中的最小值,将其移到数组末尾,逐步形成有序部分,从而实现降序排序。
  2. 排序: 交换堆的根节点(最大值或最小值)与堆的最后一个节点,并对剩余部分重新调整成堆。
    重复: 重复步骤2,直到整个数组有序。

例如利用堆排序对该数组{ 8, 5, 3, 9, 1}进行排降序,过程如下:

  1. 建小堆
    在这里插入图片描述
  2. 排序
    在这里插入图片描述

代码如下:

void AdjustDown(int* nums, int n, int parent)
{// 左孩子的索引int child = parent * 2 + 1;// 循环直到没有左孩子while (child < n){// 如果右孩子存在且比左孩子小,选择右孩子//若实现大根堆,这里nums[child + 1] < nums[child]的 < 换成 >if (child + 1 < n && nums[child + 1] < nums[child]){++child;}// 如果孩子比父亲小,交换它们的值//若实现大根堆,这里nums[child] < nums[parent]的 < 换成 >if (nums[child] < nums[parent]){// 孩子比父亲大,堆的有序性已经恢复,退出循环int tmp = nums[child];nums[child] = nums[parent];nums[parent] = tmp;}else{break;}// 更新父亲和孩子的索引parent = child;child = parent * 2 + 1;}
}void HeapSort(int* nums, int n)
{//建堆//升序,建大堆//降序,建小堆for (int i = (n - 2) / 2; i >= 0; --i){AdjustDown(nums, n, i);//非叶子节点开始向下调整}int end = n - 1;while (end > 0){//交换堆的根节点与堆的最后一个节点int tmp = nums[end];nums[end] = nums[0];nums[0] = tmp;//并对剩余部分重新调整成堆。AdjustDown(nums, end, 0);end--;}
}

总之,堆排序是一种选择排序,它利用了堆的性质:堆顶的数据,是堆中最大的数据(或者最小的数据)。该算法通过不断选择堆顶元素,将其与堆的最后一个元素交换,然后调整堆,使剩余部分重新构成堆,重复这个过程直到整个数组有序。

2 Top-K问题

Top-K 问题是在一个包含大量数据的集合中,找出前 K 个最大或最小的元素数据的问题。通常数据量都是比较大的。

  1. 关于解决TOP-K问题,我们首先想到的是对这个数据集合拍升序或者降序,然后取前 K 个数据,就能解决这个问题。该方法的缺点是不适用于数据量极大的情况。这是因为,利用排序算法,需要将数据加载到内存中,在内存中进行排序,然而当数据量大到无法一次性加载到内存中时,排序算法的效率就会受到限制。
  2. 因此,就有人提出了使用堆来解决这个问题。该算法的思想是:用数据集的前k个数据,建一个大小为 K 的小顶堆(Top K 最大问题)或大顶堆(Top K 最小问题)。依次遍历剩余n - k个元素,将元素与堆顶比较,若大于(或者小于)堆顶,则替换堆顶,并进行堆调整。这样,最终堆中的元素就是前 K 个最大或最小的元素。

例如:面试题 17.14. 最小K个数
过程如下:

  1. 用数据集的前k个数据,建一个大小为 K 的小根堆。
  2. 依次遍历剩余n - k个元素,将元素与堆顶比较,若大于堆顶,则替换堆顶,并进行堆调整。

代码如下:

 //向下调整算法void AdjustDown(int* nums, int n, int parent){int child = parent * 2 + 1;while (child < n){if (child + 1 < n && nums[child + 1] > nums[child]){++child;}if (nums[child] > nums[parent]){int tmp = nums[child];nums[child] = nums[parent];nums[parent] = tmp;}else{break;}parent = child;child = parent * 2 + 1;}}
int* smallestK(int* arr, int arrSize, int k, int* returnSize)
{int* nums = (int*)malloc(sizeof(int) * k);for (int i = 0; i < k; ++i){nums[i] = arr[i];}//前k个数建大堆for (int i = (k - 2) / 2; i >=0; --i){AdjustDown(nums, k, i);}//依次遍历剩余n - k个元素for (int i = k; i < arrSize; ++i){//将元素与堆顶比较,若大于堆顶,则替换堆顶if (k > 0 && arr[i] < nums[0]){nums[0] = arr[i];//进行堆调整AdjustDown(nums, k, 0);}}*returnSize = k;return nums;
}

至此,本片文章就结束了,若本篇内容对您有所帮助,请三连点赞,关注,收藏支持下。
创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !!!
在这里插入图片描述

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

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

相关文章

6.保留两位小数【2023.11.28】

1.问题描述 题中将给出一个具有许多小数位的浮点数&#xff0c;请将这个数字保存至小数点后两位&#xff0c;并输出。 2.解决思路 输入一个浮点数。 程序将浮点数保留两位小数并输出。 例如&#xff1a; formatted_float "{:.2f}".format(input_float)3.代码实…

11-28渗透

用nmap扫描靶机1进行主机发现 已知靶机1的主机在172.16.17.0/24下 扫描结果如下 根据扫描结果看开启的服务怀疑172.16.17.177是靶机1 浏览器访问172.16.17.177页面得到如下 我们知道织梦cms系统默认管理路径是dede&#xff0c;登陆管理后台可以通过地址172.16.17.177/dede/i…

OpenCV快速入门【完结】:总目录——初窥计算机视觉

文章目录 前言目录1. OpenCV快速入门&#xff1a;初探2. OpenCV快速入门&#xff1a;像素操作和图像变换3. OpenCV快速入门&#xff1a;绘制图形、图像金字塔和感兴趣区域4. OpenCV快速入门&#xff1a;图像滤波与边缘检测5. OpenCV快速入门&#xff1a;图像形态学操作6. OpenC…

linux用户身份切换su和 sudo

su 切换root&#xff0c;但是&#xff0c;环境变量是之前用户的 可以看到利用su切换&#xff0c;根目录还是pro1的 su - 连同环境一起切换成root&#xff0c;切换后工作目录都不一样了&#xff0c;看输入内容左侧信息&#xff0c;和第一个图片比较 -c仅执行一次命令&#xff0…

CSS问题:如何实现瀑布流布局?

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约2500字&#xff0c;整篇阅读大约需要4分钟。 本文主要内容分三部分&#xff0c;如果您只需要解决问题&#xff0c;请阅读第一、二部分即可。如果您有更多时间&#xff…

人工智能|机器学习——感知器算法原理与python实现

感知器算法是一种可以直接得到线性判别函数的线性分类方法&#xff0c;它是基于样本线性可分的要求下使用的。 一、线性可分与线性不可分 为了方便讨论&#xff0c;我们蒋样本增加了以为常数&#xff0c;得到增广样向量 y&#xff08;1;;;...;&#xff09;,则n个样本的集合为&a…

【算法刷题】Day7

文章目录 283. 移动零1089. 复写零 283. 移动零 原题链接 看到题目&#xff0c;首先看一下题干的要求&#xff0c;是在原数组内进行操作&#xff0c;平切保持非零元素的相对顺序 这个时候我们看到了示例一&#xff1a; [ 0, 1, 0, 3,12 ] 这个时候输出成为了 [ 1, 3, 12, 0, …

STK Components 二次开发- 卫星地面站

前期卫星地面站创建已经说过&#xff0c;本次说一下卫星和地面站可见性时卫星名称和轨迹线变色问题。 1.创建卫星 // Get the current TLE for the given satellite identifier. var tleList TwoLineElementSetHelper.GetTles(m_satelliteIdentifier, JulianDate.Now);// Us…

计网Lesson4 - 计算机组网模型

文章目录 计算机的连接方式1. 两台计算机的互联2. 多台计算机的互联&#xff08;旧式&#xff09;3. 多台计算机的互联 --- 集线器&#xff08;Hub&#xff09;4. 网桥5. 多台计算机的互联 --- 交换器&#xff08;Switch&#xff09; 计算机的连接方式 1. 两台计算机的互联 网…

ArrayList与顺序表的简单理解

前言----list 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。Collection也是一个接口&#xff0c;该接口中规范了后序容器中常用的一些方法&#xff0c;具体如下所示&#xff1a; Iterable也是一个接口&#xff0c;表示实现该接口的类是可以逐个元素进…

前端量子纠缠 效果炸裂 multipleWindow3dScene

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; &#x1f383; 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲 ✈️已经旅游的地点 | 新疆-乌鲁木齐、新疆-吐鲁番、广东-广州…

丽晶酒店及度假村打造绮丽之境“美食实验室”中国市场首秀

于重庆丽晶酒店以艺术与美食的碰撞演绎“对比之美”&#xff0c;感官之华 2023年11月28日&#xff0c;中国上海 ——基于对当下消费趋势的敏锐洞察&#xff0c;洲际酒店集团旗下奢华品牌丽晶酒店及度假村近年来不断焕新&#xff0c;以崭新形象缔造现代奢华的旅居体验。作为丽晶…