数据结构:堆的应用(堆排序和topk问题)

news/2025/1/9 12:36:03/

在这里插入图片描述

个人主页 : 个人主页
个人专栏 : 《数据结构》 《C语言》

文章目录

  • 堆排序
    • 建堆
    • 堆的删除思想排序
    • 代码实现
  • top k 问题
    • 思路
    • 代码实现
  • 总结


堆排序

堆排序即是 先将数据建堆,再利用堆删除的思想来排序。

  1. 将待排序数组建堆
  2. 将堆顶数据与数组尾部数据交换
  3. 调整新的堆顶数据,使其保证堆的结构不变

重复2,3步直到堆中没有数据结束。

建堆

  • 降序 建小堆 (父节点 小于等于 子节点)
  • 升序 建大堆 (父节点 大于等于 子节点)

建堆有两种思路,向上建堆 和 向下建堆。其中向下建堆优于向上建堆。

向下建堆:从最后一个子节点的父节点开始向前遍历待排序数组,不断向下调整。
如下: 对数组 {16, 72, 31, 94, 53, 23}建小堆
在这里插入图片描述
为什么不能从数组首元素开始呢? 因为向下调整的前提是 根节点的左子树 与 右子树都是大堆或小堆才可以使用。而空树 和 只有一个节点的树即可以是大堆或小堆。

堆的删除思想排序

  • 将堆顶数据 与 未排序数组尾部数据 交换
  • 向下调整新的堆顶数据,保证堆的结构不变
  • 将新未排序数组尾部数据 与 新堆顶数据交换

重复上述步骤,即可完成排序。
也可以解释为什么升序建大堆, 降序建小堆。小堆的堆顶数据永远是堆中数据最小的,将堆顶数据与未排序数组尾部交换,重复上述步骤。最小的数据就是数组最后一个元素,第二小的数据就是数字倒数第二个元素… 如此完成了降序。

如下

在这里插入图片描述

代码实现

//向下调整 小堆,假设该节点是 i, 右孩子节点是 2 * i + 1,左孩子节点是 2 * i + 2
void AdjustDown(HPDataType* data, int parent, int size)
{int child = parent * 2 + 1;while (parent < size){//防止越界                    找左右孩子中最小的if (child + 1 < size && data[child] > data[child + 1]){child++;}if (child < size && data[parent] > data[child]){swap(&data[parent], &data[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}// 对数组进行堆排序
//先构建堆    升序:大堆     降序:小堆
//如降序,先建小堆,再将堆顶数据放入数组尾部,从新选择堆顶数据
void HeapSort(int* a, int n)
{建堆向上建堆   类似于插入数据//for (int i = 0; i < n; i++)//{//	AdjustUp(a, i);//}//向下建堆   向下调整的前提:该节点的左右子树要都是大堆或小堆//倒着从第一个非叶子结点开始向下建堆//             n 是数据个数 n-1 是数组最后一个元素   (子节点 - 1) / 2 == 父节点for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, i, n);}//将堆顶数据交换数组尾部数据,再选新的堆顶,再交换新的数组尾int end = n - 1;while (end > 0){swap(&a[0], &a[end]);AdjustDown(a, 0, end);end--;}
}int main()
{int arr[] = { 16, 72, 31, 23, 94, 53 };int size = sizeof(arr) / sizeof(arr[0]);HeapSort(arr, size);for (int i = 0; i < size; i++){printf("%d ", arr[i]);}printf("\n");
}

top k 问题

top k问题就是从N个数中选出前K个数 (N远大于K)
如下:我们随机创建 10000个小于1000000的数,从中找到5个最大的数

思路

我们可以先以前5个数建小堆,再遍历9995个数,如果该数大于堆顶的数,将该数与堆顶的数替换,再向下调整保证小堆结构,继续遍历剩下的数,直到遍历完9995个数。那么堆中的5个数就是10000中最大的5个数。

代码实现

如何检查代码的正确性?
我们可以先跑一遍造数据的代码,再在其创建的文件中随机改写5个数,使其大于1000000。然后我们就可以屏蔽造数据的函数,来运行PrintTopK函数。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>void CreateNDate()
{// 造数据int n = 10000;srand((unsigned)time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = rand() % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}//从N个数中选处最大的K个数
//用前K个数建小堆(向下调整 or 向上调整),遍历N - K 个数,  (如果是大堆,那么有可能堆顶数据在一开始就是 N 个数中最大的)
//如果该数大于堆顶数据,堆顶数据 与 该数 交换在向下调整。
//遍历完 N - K 个数,那么堆中数据就是 N 个数中最大的 K 个数void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}//小堆  父节点小于等于子节点
void AdjustDown(int* data, int parent, int size)
{int child = parent * 2 + 1;while (parent < size){if (child + 1 < size && data[child] > data[child + 1]){child++;}if (child < size && data[parent] > data[child]){swap(&data[child], &data[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void PrintTopK(int k)
{const char* file = "data.txt";FILE* fin = fopen(file, "r");//读取前K个数据int* ans = (int*)malloc(sizeof(int) * (k + 1));if (ans == NULL){perror("malloc:");exit(-1);}for (int i = 0; i < k; i++){fscanf(fin, "%d", &ans[i]);}//建堆for (int i = (k - 1) / 2; i >= 0; i--){AdjustDown(ans, i, k);}while (!feof(fin)){	//读取数据int val = 0;fscanf(fin, "%d", &val);if (val > ans[0]){swap(&val, &ans[0]);AdjustDown(ans, 0, k);}}//打印数据for (int i = 0; i < k; i++){printf("%d ", ans[i]);}printf("\n");
}int main()
{CreateNDate();int k = 0;scanf("%d", &k);PrintTopK(k);return 0;
}

总结

以上就是我对于堆的应用的理解!!!
在这里插入图片描述


http://www.ppmy.cn/news/1027619.html

相关文章

Java超级玛丽小游戏制作过程讲解 第六天 创建背景类

package com.sxt;import java.awt.image.BufferedImage;public class BackGround {//当前场景要显示的图像 private BufferedImage bgImagenull;//记录当前是第几个场景 private int sort;//判断是否是最后一个场景 private boolean flag;public BackGround(){}public BackGrou…

C++中new/malloc,delete/free的区别

new和delete是操作符&#xff0c;malloc和free是库函数。 执行new实际上执行了两个操作&#xff1a;1、分配未初始化的内存空间&#xff0c;也就是调用malloc库函数。2、使用对象的构造函数对空间进行初始化&#xff0c;并返回空间的首地址。 如果第一步分配空间出现问题&…

Linux 基础(九)软件包管理

软件包管理 概念软件包管理工具RPMrpm安装rpm卸载 YUM&#xff08;推荐&#xff09;YUM仓库YUM管理软件 概念 各个系统都有自己的软件包管理工具&#xff0c;方便用户管理&#xff0c;使用各种软件&#xff1b; 只是大部分Windows用户可能并没有太关注&#xff0c;其实也是有的…

flask---》更多查询方式/连表查询/原生sql(django-orm如何执行原生sql)/flask-sqlalchemy

更多查询方式 #1 查询&#xff1a; filer:写条件 filter_by&#xff1a;等于的值 # 查询所有 是list对象 res session.query(User).all() # 是个普通列表 print(type(res)) print(len(res))# 2 只查询某几个字段 # select name as xx,email from user; res session.…

Vue电商项目--组件通信

组件通信6种方式 第一种&#xff1a;props 适用于的场景&#xff1a;父子组件通信 注意事项&#xff1a; 如果父组件给子组件传递数据&#xff08;函数&#xff09;&#xff1a;本质其实是子组件给父组件传递数据 如果父组件给子组件传递的数据&#xff08;非函数&#xf…

股票量化分析工具QTYX使用攻略——盘口异动实时监测(更新2.6.8)

QTYX简介‍‍‍ 股票量化交易系统QTYX是一个即可以用于学习&#xff0c;也可以用于实战炒股分析的系统。 分享QTYX系统目的是提供给大家一个搭建量化系统的模版&#xff0c;最终帮助大家搭建属于自己的系统。因此我们提供源码&#xff0c;可以根据自己的风格二次开发。 关于QTY…

2023国赛数学建模A题思路分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…

机器学习笔记 - 基于PyTorch + 类似ResNet的单目标检测

一、获取并了解数据 我们将处理年龄相关性黄斑变性 (AMD) 患者的眼部图像。 数据集下载地址,从下面的地址中,找到iChallenge-AMD,然后下载。 Baidu Research Open-Access Dataset - DownloadDownload Baidu Research Open-Access Datasethttps://ai.baidu.com/bro…