堆排序——高效解决TOP-K问题

在这里插入图片描述在这里插入图片描述

.

个人主页:晓风飞
专栏:数据结构|Linux|C语言
路漫漫其修远兮,吾将上下而求索


文章目录

  • 引言
  • 什么是堆?
  • 建堆
  • 堆排序:
    • 排序的最终结果
  • 堆排序实现
    • 函数声明
    • 交换函数 `Swap`
    • 下沉调整 `DnAdd`
    • 堆排序函数 `HeapSort`
    • 主函数
  • 文件中找TopK问题
    • 什么是TOP-K问题
    • 堆排序的解决方案
    • 操作应用
    • 结论


引言

在数据结构和算法的世界中,排序是一个基本而重要的概念。堆排序是一种高效的排序算法,它利用堆这一数据结构的特性来实现。在这篇文章中,我们将深入探索堆排序的原理,并通过C语言示例来展示它的实现。


什么是堆?

堆是一种特殊的完全二叉树,其中每个父节点的值都大于或等于其子节点的值(最大堆),或者每个父节点的值都小于或等于其子节点的值(最小堆)。在堆排序中,我们通常使用最大堆。


建堆

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


堆排序:

将堆顶元素(最大值)与最后一个元素交换,然后减少堆的大小,并重新对堆顶元素执行下沉操作。重复此过程,直到堆的大小为1。建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。
以下是使用实现堆排序的基本步骤:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

排序的最终结果

在这里插入图片描述


堆排序实现

函数声明

交换函数 :
void Swap(HPDataType* p1, HPDataType* p2)
下沉调整 :
void DnAdd(HPDataType* a, HPDataType parent, int size)
堆排序函数:
void HeapSort(int* a, int n)

交换函数 Swap

void Swap(HPDataType* p1, HPDataType* p2) {HPDataType tmp = 0;  // 临时变量用于交换tmp = *p1;*p1 = *p2;*p2 = tmp;
}

Swap 函数的作用是交换两个元素的值。这在堆排序中非常重要,特别是在删除堆顶元素或重构堆的过程中。此函数通过传递指向数据的指针来直接修改原数组。

下沉调整 DnAdd

void DnAdd(HPDataType* a, HPDataType parent, int size) {int child = parent * 2 + 1; // 找到左子节点while (child < size) {// 检查右子节点是否存在,以及比较左右两个子节点的值if (child + 1 < size && a[child + 1] > a[child]) {child++; // 选择较大的子节点}// 如果子节点大于父节点,则需要交换if (a[child] > a[parent]) {Swap(&a[child], &a[parent]); // 交换父子节点parent = child; // 更新父节点位置child = parent * 2 + 1;} else {break; // 如果不需要交换,则终止循环}}
}

DnAdd 函数实现了堆的下沉调整,是构建和维护堆的关键操作。如果子节点的值大于父节点的值,则需要进行交换,以确保维护最大堆的性质。

堆排序函数 HeapSort

void HeapSort(int* a, int n) {// 构建初始大顶堆for (int i = (n / 2) - 1; i >= 0; i--) {DnAdd(a, i, n);}// 从堆中逐个移除元素并进行排序for (int end = n - 1; end > 0; end--) {Swap(&a[0], &a[end]); // 将最大的元素(堆顶)移动到数组的末尾DnAdd(a, 0, end); // 对剩余的堆进行向下调整}
}

HeapSort 函数首先通过调用 DnAdd 函数建立一个大顶堆。之后,通过不断移除堆顶元素(数组中的最大元素)并将其移动到数组的末尾,然后再次调用 DnAdd 函数进行下沉调整,最终达到整个数组的排序目的。

主函数

Copy code
int main() {int arr[] = { 8, 6, 4, 2, 0, 9, 4 };HeapSort(arr, sizeof(arr) / sizeof(arr[0]));for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {printf("%d ", arr[i]);}
}

在主函数中,我们定义了一个未排序的数组 arr 并调用了 HeapSort 函数对其进行排序。排序完成后,使用一个循环来打印排序后的数组元素。通过main函数中的测试数组,我们可以看到HeapSort函数如何将无序数组转换成一个有序序列。我们也可以通过更换数组中的元素来测试不同的数据集。


文件中找TopK问题

什么是TOP-K问题

TOP-K问题是指在一个大数据集中找到前K个最大或最小的元素。这个问题在多个领域都非常常见,比如排名、选举、统计和游戏等。常见的例子包括找到考试成绩中的前10名、世界500强企业或者游戏中最活跃的100名玩家。

当数据量非常大时,简单的排序方法可能会因为数据量超过内存限制而变得不可行。此外,完整的排序操作的时间复杂度为
O(nlogn),这在数据量极大时效率低下。

堆排序的解决方案

堆排序提供了一个更为高效的解决方案,时间复杂度为O(nlogK),这对于大数据集来说是一个巨大的提升。解决TOP-K问题的基本思路是:

用数据集合中前K个元素来建堆:
如果我们需要找到前K个最大的元素,则建立一个最小堆。
如果我们需要找到前K个最小的元素,则建立一个最大堆。
用剩余的N-K个元素依次与堆顶元素比较:
如果当前元素比堆顶元素大(在寻找最大元素时)或小(在寻找最小元素时),则将其与堆顶元素替换,并重新调整堆。
提取堆中的元素:

经过上述过程后,堆中剩余的K个元素就是我们要找的前K个最大或最小的元素。

操作应用

在文件中建立100000个数,查找前5个数最大的数

void PrintTopK(const char* file, int k)
{FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}// 建一个k个数的最小堆int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc error");fclose(fout); // 记得关闭文件指针return;}// 读取前k个数,以构建最小堆for (int i = 0; i < k; i++){if (fscanf(fout, "%d", &minheap[i]) != 1) // 检查fscanf的返回值{perror("fscanf error");free(minheap);fclose(fout);return;}UpAdd(minheap, i); // 由于是读取前k个数,这里应该是UpAdd}// 遍历文件中剩余的数,维护最小堆int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minheap[0]) // 只有新的数比堆顶大时,才替换并进行下沉{minheap[0] = x;DnAdd(minheap, 0, k); // 注意这里是对堆顶进行下沉,所以传入的应该是0}}// 输出结果HeapSort(minheap, k); // 排序最小堆,使之按照顺序输出for (int i = 0; i < k; i++){printf("%d ", minheap[i]);}printf("\n");free(minheap); // 释放内存fclose(fout); // 关闭文件
}

结论

堆排序是一种非常有效的排序算法,特别适用于大数据集。通过利用堆的属性,它能够以 (O(n \log n)) 的时间复杂度进行排序。这篇文章通过C语言示例展示了堆排序的实现,希望能帮助你更好地理解这个强大的算法。

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

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

相关文章

SpringBoot+Vue实现对称加密和非对称加密

我们先来了解一下什么是对称加密和非对称加密&#xff0c;以及两者的优缺点 对称加密 使用同一个密钥对消息进行加密解密 优点&#xff1a;加密和解密的速度快&#xff0c;适合于数据量大的加解密 缺点&#xff1a;密钥在网络传输中可能被泄露&#xff0c;因此安全性相对较低…

C++核心编程三:函数提高(持续更新)

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;C从基础到进阶 &#x1f319;C核心编程&#x1f30f;1 函数提高&#x1f384;1.1 函数默认参数&#x1f384;1.2 函数占位参数&#x1f384;1.3 函数重载&#x1f349;1.3.1 函数重载概述&…

护眼灯有蓝光吗?防蓝光护眼台灯推荐

护眼台灯是家长为孩子购买的常见用品之一&#xff0c;但对于它的了解却不够深入&#xff0c;很多人购买之后反而容易出现眼睛疲劳、不适的情况&#xff01;据了解&#xff0c;主要的原因是因为在选择护眼台灯时&#xff0c;大多数人没有专业知识&#xff0c;没有买到合适的护眼…

012集:三目运算符实例讲解(if else)及for、while循环—python基础入门实例

Python也有自己的三目运算符&#xff1a; 条件为真时的结果 if 判段的条件 else 条件为假时的结果 即&#xff1a;Python可以通过if语句来实现三目运算符的功能&#xff0c;因此可以把这种if语句当做三目运算符&#xff0c;具体语法格式如下&#xff1a; 返回True执行 if 表达…

Github搭建图床 github搭建静态资源库 免费CDN加速 github搭建图床使用 jsdelivr CDN免费加速访问

Github搭建图床 github搭建静态资源库 免费CDN加速 github搭建图床使用 jsdelivr CDN免费加速访问 前言1、创建仓库2、开启 gh-pages页面功能3、访问测试 前言 写博客文章时&#xff0c;图片的上传和存放是一个问题&#xff0c;使用小众第三方图床&#xff0c;怕不稳定和倒闭&…

.net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别

//全局过滤器 builder.Services.AddMvc(m > { m.Filters.Add<AllResultFilter>(); }); 1、实现过滤器 public class AllResultFilter : IResultFilter {/// <summary>/// 结果执行后方法/// 不可更改结果/// </summary>/// <param name"con…

springboot+mysql大学生就业推荐系统-计算机毕业设计源码01535

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对学生就业管理等问题&#xff0c;对学生就业…

扒开MySQL的源码,探索MVCC实现方式

下载MySQL源码 没有什么比源码更靠谱的了&#xff0c;所以我们先把源码下载下来&#xff0c;后期验证使用MySQL源码下载 MVCC是什么 mvvc全称是multi-version concurrency control&#xff08;多版本并发控制&#xff09;&#xff0c;主要用于处理读写并发冲突的问题。 MVC…

大数据开发之Hive(企业级调优)

第 10 章&#xff1a;企业级调优 创建测试用例 1、建大表、小表和JOIN后表的语句 // 创建大表 create table bigtable(id bigint, t bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by \t; //…

Asp .Net Core 系列:基于 Swashbuckle.AspNetCore 包 集成 Swagger

什么是 Swagger? Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。它提供了一种规范的方式来定义、构建和文档化 RESTful Web 服务&#xff0c;使客户端能够发现和理解各种服务的功能。Swagger 的目标是使部署管理和使用功…

Halcon滤波器 laplace 算子

Halcon滤波器 laplace 算子 使用laplace 算子对图像进行二次求导&#xff0c;会在边缘产生零点&#xff0c;因此该算子常常与zero_crossing算子配合使用。求出这些零点&#xff0c;也就得到了图像的边缘。同时&#xff0c;由于laplace算子对孤立像素的响应要比对边缘或线的响应…

【SpringBoot系列】JDK动态代理

🤵‍♂️ 个人主页:@香菜的个人主页,加 ischongxin ,备注csdn ✍🏻作者简介:csdn 认证博客专家,游戏开发领域优质创作者,华为云享专家,2021年度华为云年度十佳博主 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收…

kafka除了作为消息队列还能做什么?

Kafka 最初是为大规模处理日志而构建的。它可以保留消息直到过期&#xff0c;并让各个消费者按照自己的节奏提取消息。 与其之前的竞品不同&#xff0c;Kafka 不仅仅是一个消息队列&#xff0c;它还是一个适用于各种情况的开源事件流平台。 让我们回顾一下流行的 Kafka 用例。 …

C语言经典算法之冒泡排序算法

目录 前言 建议&#xff1a; 简介&#xff1a; 一、代码实现 二、时空复杂度 时间复杂度&#xff1a; 空间复杂度&#xff1a; 总结&#xff1a; 前言 建议&#xff1a; 1.学习算法最重要的是理解算法的每一步&#xff0c;而不是记住算法。 2.建议读者学习算法的时候…

CNN:Convolutional Neural Network(下)

目录 1 CNN 学到的是什么 1.1 Convolution 中的参数 1.2 FFN 中的参数 1.3 Output 2 Deep Dream 3 Deep Style 4 More Application 4.1 AlphaGo 4.2 Speech 4.3 Text 原视频&#xff1a;李宏毅 2020&#xff1a;Convolutional Neural Network 本博客属于学…

计算机毕业设计----SSH实现简单在线听音乐收藏管理系统

项目介绍 项目分为管理员与普通用户两种角色&#xff0c; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,歌曲管理等功能。 用户角色包含以下功能&#xff1a; 按分类查看,添加歌单,用户登录等功能。 环境需要 1.运行环境&#xff1a;最好是java jdk 1.8&…

shell从入门到精通

系列文章目录 shell从入门到精通 shell从入门到精通 系列文章目录一、diff 用法 &#xff08;一般作补丁,用补丁的方式更新脚本&#xff09;1.1参数a添加1.2 参数c更改1.3参数d删除1.4参数a和d的对比1.5参数b&#xff08;忽略空格&#xff09;1.6 参数B&#xff08;忽略空行&a…

打造创新的金融数据平台,加速数字化和智能化转型丨PingCAP 官网金融行业专区上线

自诞生以来&#xff0c;TiDB 的原生分布式架构在强一致性、高可用性和可扩展性等方面与金融级业务需求高度契合&#xff0c;早期版本即为包括北京银行在内的金融用户提供服务。 TiDB 的核心能力始终源自与中国金融用户的共同创造。作为金融级分布式数据库&#xff0c;TiDB 在国…

【pytorch】使用pytorch构建线性回归模型-了解计算图和自动梯度

使用pytorch构建线性回归模型 线性方程的一般形式 衡量线性损失的一般形式-均方误差 pytorch中计算图的作用和优势 在 PyTorch 中&#xff0c;计算图&#xff08;Computational Graph&#xff09;是一种用于表示神经网络运算的数据结构。每个节点代表一个操作&#xff0c;例如…

药物“出气”可知|ZL-005大小鼠雾化给药仪

雾化吸入给药是一种通过装置使药物进入肺部局部或全身发挥作用的给药方式。相较于口服药剂&#xff0c;雾化吸入给药可避免首过效应和药剂破坏&#xff0c;提高药物生物利用度。 而ZL-005大小鼠雾化给药仪&#xff0c;则是新一代小动物雾化装置的理想选择&#xff0c;可完成动…