堆的应用(堆排序,TOP-K问题)详细讲解

在这里插入图片描述
所有人都关心我飞的高不高,只有我妈关心我翅膀硬不硬

一、堆的应用

1. 堆排序

1.1 建堆

1.2 利用堆删除思想来进行排序

2.TOP-K问题

二、完结撒❀

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–
学习一个知识,我们起码要直到它的用途,那样才算学会了

一、堆的应用

1.堆排序

大家肯定都学过冒泡排序,快速排序等等,在学完堆之后我们也可以用堆来实现数据排序。
(ps:冒泡排序的时间复杂度:O(N^2))

1.1 建堆

升序:建大堆
降序:建小堆

建堆方式可能与大家预想的不太一样,但确实如此,升序我们需要建大堆,降序我们建小堆,再利用堆删除的思想进行排序,时间复杂度会低很多。

1.2 利用堆删除思想来进行排序

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

void Swap(HPDataType* px, HPDataType* py)
{HPDataType tmp = *px;*px = *py;*py = tmp;
}//向下调整O(logN)
void AdJustDown(HPDataType* a, int n, int parent)
{//从左孩子开始,child为小孩子那个int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child] > a[child + 1]){++child;}if (a[child] < a[parent])//小堆<,大堆>{Swap(&a[parent], &a[child]);parent = child;child = child * 2 + 1;}else{break;}}
}//升序  大堆 O(N*logN)
//降序  小堆 O(N*logN)
void HeapSort(HPDataType* a, int n)
{//根据数组直接建堆 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdJustDown(a, n, i);}//交换根和尾的位置,再向下对前end(end每次少一个)的数进行调整 O(N*logN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdJustDown(a, end, 0);--end;}
}

只需要将要排序的数组地址和数组元素的总个数作为实参传过来,并且会向下调整,就可以实现排序。
这样的堆排序时间复杂赋为O(N*logN)

我们拿一个数组以降序排小堆为大家举例讲解:

int arr[] = {5,2,3,6,1,4,7}

逻辑图解:

在这里插入图片描述
因为每次向下调整,根一定是数组中最小值,将最小值与当前数组访问的尾坐标进行交换,直到end为0,数组中的元素便以排好顺序。

2.TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

1. 用数据集合中前K个元素来建堆

             前k个最大的元素,则建小堆前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

为了更好的给大家讲解,我们可以现根据文件操作手动造出一些数据来

代码如下:

//造数据
void CreateNDate()
{int n = 10000;srand(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();fprintf(fin, "%d\n", x);}fclose(fin);
}

以读的方式在data.txt文件中造出10000个随机数
效果如下:
在这里插入图片描述当然,如果感觉10000个数据不够多,可以手动添加更多数据。

接下来我们就在这10000个数据中进行TOp-K问题的讲解

如何在这10000个数据中选出前K个最大的数呢?

上面对TOP-K问题的讲解已经给出了思路

我们可以先看一下代码实现:

//按照大小选出前k个值
void Tokp()
{//选出前k个最大数据printf("请输入前几个最大的值:>");int k = 0;scanf("%d", &k);//将数据中前k个数据存入到创建的minheap数组中const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc fail");return;}for (int i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);}//建堆(向下建堆)for (int i = (k - 1 - 1) / 2; i >= 0; --i){AdJustDown(minheap, k, i);}//判断大小进行替换int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (minheap[0] < x){minheap[0] = x;AdJustDown(minheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", minheap[i]);}fclose(fout);}

这里对文件操作函数比如:fscanf,fprintf等不太熟悉的同学建议去官网进行查询如何使用之后再进行Top-K问题的学习
官网网址:cplusplus

假设我们要前10个最大的数据,那么输入k为10.
实现逻辑步骤:

1.输入k为10
2.以读的方式打开data.txt文件
3.创建一个10个数据空间大小(单位为字节)的数组
4.将文件中前10个数存入到数组中
5.对数组进行向下建堆(这里我们要前10个最大的数据,所以要建小堆)
6.将根节点以此与剩余9990个数据进行对比,大于根节点就进行替换再对前十个数据进行向下调整
7.打印数组,关闭文件

因为创建的是小堆,那么根节点的值一定是堆中最小的,如果后续数据大于根节点的值,替换后再向下调整,最后对比完数据前十个建好的小堆数据就是这10000个数据中最大的10个。

调用函数打印结果:
在这里插入图片描述
因为10000个数据比较大,并且我们也并不知道这10000个数据中都有哪些数组,心里没有底
那么会不会有同学怀疑打印出的数值是否正确呢?

下面我教大家怎么检验所敲的该函数是否正确
我们可以在造数据的函数中做一些手脚
代码如下:

//造数据
void CreateNDate()
{int n = 100000;srand(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()+i)%10000;//检验程序准确fprintf(fin, "%d\n", x);}fclose(fin);
}

这次我们将造出来的随机数该成100000个(判断数据增多会不会出现BUG)
再将造出来的每一个随机数都加上i并且余上10000

因为随机数所返回的数值大小范围为30000左右,我们为了让造出来的数更加随机,就每次加上i
余上10000的目的是为了让造出来的数字都在0~9999之间

之后我们直接执行该函数

执行后打开data.txt文件夹,我们会看到100000个数值在0~9999之间的数组

我们在该文件夹中随机更改5个数字,将这5个数字都改成大于10000的值,越大越好,之后保存

再对该文件里的所有数值进行Tokp,假设输入k为10,那么最后出来结构只要包含我们所更改的5个数据,就说明该函数打印前k个最大数据是正确的。

二、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤
在这里插入图片描述

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

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

相关文章

斜率优化dp 笔记

任务安排1 有 N 个任务排成一个序列在一台机器上等待执行&#xff0c;它们的顺序不得改变。 机器会把这 N 个任务分成若干批&#xff0c;每一批包含连续的若干个任务。 从时刻 00 开始&#xff0c;任务被分批加工&#xff0c;执行第 i 个任务所需的时间是 Ti。 另外&#x…

【LVGL-字库应用】

LVGL-中文字库应用 ■ LVGL-内部字库■ LVGL 内部字库的使用流程&#xff1a; ■ LVGL-自定义字库■ 方法一&#xff1a;C 语言数组&#xff08;内部读取&#xff09;-在线转换工具■ 方法二&#xff1a;C 语言数组&#xff08;内部读取&#xff09;-利用离线字体转换软件&…

销售的业绩和合同无法统一管理可以通过系统实现吗?

这个问题我们在日常管理中也遇到过&#xff0c;在没有使用软件之前&#xff0c;合同原件没有专人负责打理&#xff0c;销售人员签了合同后&#xff0c;直接把原件随手放在柜子里&#xff0c;或者把数据记录到excel表中。 但是每个人的工作习惯不一样&#xff0c;记录的表格也不…

Spring boot 发送文本邮件 和 html模板邮件

Spring boot 发送文本邮件 和 html模板邮件 提示&#xff1a;这里使用 spring-boot-starter-mail 发送文本邮件 和 html模板邮件 文章目录 Spring boot 发送文本邮件 和 html模板邮件一、开启QQ邮箱里的POP3/SMTP服务①&#xff1a;开启步骤 二、简单配置①&#xff1a;引入依赖…

Linux系统使用Docker部署MinIO结合内网穿透实现公网访问本地存储服务

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

①胃癌单细胞和配对转录组揭示胃肿瘤微环境(文献和数据)

目录 文献内容 胃癌细胞微环境格局 肿瘤基质的特征存在活化的成纤维细胞 肿瘤内皮细胞与血管生成途径 巨噬细胞显示炎症二分法 淋巴细胞转向免疫抑制和分化表型 细胞通信网络揭示了胃癌发展的潜在驱动因素 胃癌免疫治疗批量RNA-seq的整合揭示了细胞和分子的反应倾向 数…

Web漏洞-深入WAF注入绕过

目录 简要其他测试绕过 方式一:白名单&#xff08;实战中意义不大&#xff09; 方式二:静态资源 方式三: url白名单 方式四:爬虫白名单 #阿里云盾防SQL注入简要分析 #安全狗云盾SQL注入插件脚本编写 在攻防实战中&#xff0c;往往需要掌握一些特性&#xff0c;比如服务…

Zabbix6 - Centos7源码编译部署HA高可用集群手册

Zabbix6 - Centos7源码编译部署HA高可用集群手册 HA高可用集群 总所周知,在我们IT运维的圈圈中,HA高可用集群服务算是逼格最高的吧也是运维里保障力度最大的环境。 HA是HighlyAvailable缩写,是双机集群系统简称,提高可用性集群,是保证业务连续性的有效解决方案,一般有两个…

6款好用的AI写作助手,为大家解决创作困难

写作出高质量的文章对于每个创作者来说是非常重要&#xff0c;但有时候在面对繁重的写作任务时&#xff0c;大家可能会陷入创作困境&#xff0c;思绪纷乱、灵感枯竭。&#xff0c;但随着人工智能技术的不断发展&#xff0c;AI写作助手已经成为了许多创作者的得力帮手&#xff0…

Java代码混淆技术在应用程序保护中的关键作用与应用

摘要 本文探讨了代码混淆在保护Java代码安全性和知识产权方面的重要意义。通过混淆技术&#xff0c;可以有效防止代码被反编译、逆向工程或恶意篡改&#xff0c;提高代码的安全性。常见的Java代码混淆工具如IPAGuard、Allatori、DashO、Zelix KlassMaster和yGuard等&#xff0…

Acwing_795前缀和 【一维前缀和】+【模板】二维前缀和

Acwing_795前缀和 【一维前缀和】 题目&#xff1a; 代码&#xff1a; #include <bits/stdc.h> #define int long long #define INF 0X3f3f3f3f #define endl \n using namespace std; const int N 100010; int arr[N];int n,m; int l,r; signed main(){std::ios::s…

OpenHarmony实战开发-Web组件的使用

介绍 本篇Codelab使用ArkTS语言实现一个简单的免登录过程&#xff0c;向大家介绍基本的cookie管理操作。主要包含以下功能&#xff1a; 获取指定url对应的cookie的值。设置cookie。清除所有cookie。免登录访问账户中心。 原理说明 本应用旨在说明Web组件中cookie的管理操作。…

Siemens S7-1500TCPU 运动机构系统功能简介

目录 引言&#xff1a; 1.0 术语定义 2.0 基本知识 2.1 运动系统工艺对象 2.2 坐标系与标架 3.0 运动机构系统类型 3.1 直角坐标型 3.2 轮腿型 3.3 平面关节型 3.4 关节型 3.5 并联型 3.6 圆柱坐标型 3.7 三轴型 4.0 运动系统的运动 4.1 运动类型 4.1.1 线性运动…

OpenHarmony中的LLDB高性能调试器

概述 LLDB&#xff08;Low Lever Debugger&#xff09;是新一代高性能调试器。详细说明参考 LLDB官方文档 。 当前OpenHarmony中的LLDB工具是在 llvm15.0.4 基础上适配演进出来的工具&#xff0c;是HUAWEI DevEco Studio工具中默认的调试器&#xff0c;支持调试C和C应用。 工…

C++ Primer (第五版)第七章习题部分答案(上)

在我自学C过程中&#xff0c;我选择了CPrimer这本书&#xff0c;并对部分代码习题进行了求解以及运行结果。接下来几个月我将为大家定时按章节更新习题答案与运行结果&#xff0c;运行环境&#xff08;Visual Studio Code&#xff0c;windows 11&#xff09;&#xff1a; C P…

open Gauss 数据库-03 openGauss数据库维护管理指导手册

目录 前言 openGauss数据库维护管理 1 操作系统参数检查 1.1 实验介绍 1.2 场景设置及操作步骤 2 openGauss 运行健康状态检查 2.1 实验介绍 2.2 场景设置及操作步骤 3 数据库性能检查 3.1 实验介绍 3.2 通过 gs_checkperf 工具来检查数据库性能 3.3 通过 EXPLAIN …

探索父进程和子进程

文章目录 通过系统调用查看进程PID父进程、子进程 通过系统调用创建进程-fork初识为什么fork给父进程返回子进程的PID&#xff0c;给子进程返回0fork函数如何做到返回两个值一个变量为什么同时会有两个返回值&#xff1f;bash总结 通过系统调用查看进程PID getpid()函数可以获…

Go语言学习Day6:数组与切片

名人说&#xff1a;莫愁千里路&#xff0c;自有到来风。 ——钱珝 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1. 数组① 什么是数组② 数组的声明③ 初始化数组的几种方式④ 遍历数组元素⑤ 数组为值类型⑥ 数…

Go语言爬虫实战(线程池)

Go语言爬虫实战 目标 利用go语言爬取指定网站的图片。实现爬取网站任意页面所有所需的图片。实现使用go语言线程池开启多个线程爬取图片内容。最后实现创建多个文件夹存储图片。 爬取网站图片 步骤 对指定URL发去GET请求&#xff0c;获取对应的响应。 resp, err : http.Get(…

SQL Server 实验二:数据库视图的创建和使用

目录 第一关 相关知识 什么是表 操作数据表 创建数据表 插入数据 修改表结构 删除数据表 编程要求 第一关实验代码&#xff1a; 第二关 相关知识 视图是什么 视图的优缺点 视图的优点 视图的缺点 操作视图 创建视图 通过视图向基本表中插入数据 通过视图修改基本表的…