【数据结构】堆排序和top-k问题

目录

1.堆排序 

2.top-k问题


1.堆排序 

我们已经介绍了向上调整算法和向下调整算法建堆,可以建一个小堆或大堆,对于这种方式建立的大堆或小堆,我们只能选出最大的和最小的数,对于次大或次小的数,只能重新建堆,要想得到这组数据的降序或升序,只能不断建堆选取数据,但这种操作时间复杂度太高,每次建堆的时间复杂度为O(N)

如何建立一个有序的堆(以升序为例)?

  1. 升序建大堆,降序建小堆,使用向上调整算法或向下调整算法建大堆(小堆)
  2. 已经建成大堆,堆顶为这组数据中的最大元素,但堆中的数字并不是严格升序
  3. 堆顶元素和最后一个叶子节点交换,此时最后一个叶子节点为这组数据中的最大值,从堆顶向下调整选出次大的数,此时次大的数在根节点的位置,与倒数第二个叶子节点交换,此时倒数第二个叶子节点为这组数据中的次大值,后续依次类似处理

以以下数据为例

int a[] = { 4,1,7,8,15,34,19,27,25,65 };

step1:使用向上调整算法建大堆

这组数据逻辑上可以看作一棵完全二叉树的结构

向上调整建堆:向上调整算法从最后一个叶子节点开始调整,每次使一个元素到达其相对子节点较大的位置

第一次向上调整

第二次向上调整

第三次向上调整

第四次向上调整

第五次向上调整

第六次向上调整

第六次之后,所有的的节点都小于父节点,会进入循环,但不存在交换操作,循环在child<=0时结束;因此建大堆的操作完成,我们选出了最大的数,并存放在堆顶

step2:调整选数

1.堆顶元素和最后一个叶子节点交换,此时最后一个叶子节点为这组数据中的最大值,从堆顶向下调整选出次大的数,此时次大的数在根节点的位置

2.忽略最后一个存放最大值的节点,交换堆顶元素与倒数第二个叶子节点,此时倒数第二个叶子节点为这组数据中的次大值,堆顶元素向下调整选出第三大的数,此时第三大的数在根节点的位置

重复上述操作,就可以建立一个升序的堆

堆排序的本质:选择排序,利用堆的性质,依次选数,从后向前排

参考代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
typedef int HPDataType;//交换两个节点
void Swap(HPDataType* p1, HPDataType* p2)
{assert(p1 && p2);HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent)
{assert(a);int  maxchild = parent * 2 + 1;while (maxchild < n){//找到左右孩子中的较大者if (maxchild + 1 < n && a[maxchild] < a[maxchild + 1]){maxchild = maxchild + 1;//更新最大孩子}if (a[parent] < a[maxchild]){Swap(&a[parent], &a[maxchild]);//更新父子节点parent = maxchild;maxchild = parent * 2 + 1;}//当父子满足大堆关系时,不进行交换else{break;}}
}//向上调整算法
void AdjustUp(HPDataType* a, int child)
{assert(a);int parent = (child - 1) / 2;while (child > 0){//大堆if (a[parent] < a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void HeapSort(HPDataType* a, HPDataType n)
{//向上调整建大堆int  i = 0;for (i = n - 1; i >= 0; i--){AdjustUp(a, i);}//选数i = 1;while (i < n){Swap(&a[0], &a[n - i]);AdjustDown(a, n - i, 0);i++;}
}int main()
{HPDataType a[] = { 4,1,7,8,15,34,19,27,25,65 };int len = sizeof(a) / sizeof(a[0]);HeapSort(&a, len);//打印for (int i = 0; i < len; i++){printf("%d ", a[i]);}return 0;
}

2.top-k问题

top-k问题:即求数据集合中前k个最大或者最小的元素 ,一般情况下数据量较大

如:专业前十名,世界500强,富豪榜前十名,游戏排行榜前十等

对于top-k问题,最简单直接的方式就是排序,但当数据量非常大,排序的方式就不可行,此时使用堆来解决,思路如下:

选前k个最大的:

1️⃣建大堆:

选出前k个最大的数,先建立一个大堆,使用堆排序,需要迭代k次,从后向前取前k个数即可,这种方法在N很大,k很小时,即在很大的数据集合中选出前几名,我们需要将这个数据集合先存储起来,可能出现内存存储不下,只能存储在磁盘中的情况

2️⃣ 建小堆:

  1. 用数据集合中的前k个数建立k个数的小堆
  2. 依次遍历后续N-k个数,与堆顶元素(最小的数)比较,若大于堆顶元素,则替换并向下调整到合适位置,向下调整后仍为k个数的小堆且堆顶元素为k个数中的最小数
  3. 重复上述操作,直到遍历完数据集合中的剩余元素

Note:

使用上述方法可以筛选出前k个最大的数,但选出来的这k个数是以小堆的方式存储的,但并不是有序排列的,如果需要其有序排列,使用上述堆排序的方法将其排序即可

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <time.h>
#include<assert.h>
#include<stdlib.h>
typedef int HPDataType;//交换两个节点
void Swap(HPDataType* p1, HPDataType* p2)
{assert(p1 && p2);HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent)
{assert(a);int  maxchild = parent * 2 + 1;while (maxchild < n){//找到左右孩子中的较大者if (maxchild + 1 < n && a[maxchild] < a[maxchild + 1]){maxchild = maxchild + 1;//更新最大孩子}if (a[parent] < a[maxchild]){Swap(&a[parent], &a[maxchild]);//更新父子节点parent = maxchild;maxchild = parent * 2 + 1;}//当父子满足大堆关系时,不进行交换else{break;}}
}//向上调整算法
void AdjustUp(HPDataType* a, int child)
{assert(a);int parent = (child - 1) / 2;while (child > 0){//大堆if (a[parent] < a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//创建数据文件
void CreateDataFile(const char* filename, int N)
{FILE* fin = fopen(filename, "w");if (fin == NULL){perror("fopen fail");return;}srand(time);for (int i = 0; i < N; i++){fprintf(fin, "%d\n", rand()%100);//产生100以内的随机数}fclose(fin);
}void PrintTopK(const char* filename, int k)
{assert(filename);FILE* fout = fopen(filename, "r");if (fout == NULL){perror("fopen fail");return;}//文件读取成功//为堆开辟空间int* minHeap = (int*)malloc(sizeof(int) * k);if (minHeap == NULL){perror("malloc fail");return;}//读取前k个数到为堆开辟的空间中for (int i = 0; i < k; i++){fscanf(fout, "%d", &minHeap[i]);}//前k个数建小堆.向上调整算法for (int i = k - 1; i >= 0; --i){AdjustUp(minHeap, i);}//依次访问后N-k个数int val = 0;while (fscanf(fout, "%d", &val) != EOF){//出现比k个数中最小数大的数,替换并向下调整if (val > minHeap[0]){minHeap[0] = val;AdjustDown(minHeap, k, 0);}}//打印前k个数for (int i = 0; i < k; i++){printf("%d ", minHeap[i]);}free(minHeap);fclose(fout);}int main()
{const char* filename = "Data.text";int N = 100;int k = 10;//CreateDataFile(filename, N);PrintTopK(filename, k);return 0;
}

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

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

相关文章

详解Avast Driver Updater:电脑驱动更新工具的利器还是多余的软件?

亲爱的读者朋友们&#xff0c;你是不是经常为电脑的驱动问题而烦恼&#xff1f;如果是的话&#xff0c;你可能会对这款软件——Avast Driver Updater 电脑驱动更新工具感兴趣。但在你决定尝试之前&#xff0c;不妨先和我一起深入探讨一下它的优点、缺点以及它适用的使用场景。 …

UE5.1编辑器拓展【二、脚本化资产行为,快速更改资产名字,1.直接添加前缀或后缀2.通过资产类判断添加修改前缀】

目录 了解相关的函数 第一种做法&#xff1a;自定义添加选择资产的前缀或后缀 代码 效果 第二种做法&#xff1a;通过映射来获取资产类型添加前缀和修改前缀 映射代码 代码 效果 在之前一章中&#xff0c;我们创建了插件&#xff0c;用来扩展编辑器的使用&#xff1a; …

十八,镜面IBL-打印预过滤环境贴图

前面打印了各个级别的hdr环境贴图&#xff0c;也能看到预过滤环境贴图&#xff0c;现在进行打印各个级别的预过滤环境贴图。 运行结果如下 代码如下&#xff1a; #include <osg/TextureCubeMap> #include <osg/TexGen> #include <osg/TexEnvCombine> #…

JUC——并发编程—第二部分

集合类不安全 list不安全 //报错 java.util.ConcurrentModificationException public class ListTest {public static void main(String[] args) {List<String> list new CopyOnWriteArrayList<>();//并发下Arrayist边读边写会不安全的/*** 解决方案&#xff1a…

iPhone苹果手机复制粘贴内容提示弹窗如何取消关闭提醒?

经常使用草柴APP查询淘宝、天猫、京东商品优惠券拿购物返利的iPhone苹果手机用户&#xff0c;复制商品链接后打开草柴APP粘贴商品链接查券时总是弹窗提示粘贴内容&#xff0c;为此很多苹果iPhone手机用户联系客服询问如何关闭iPhone苹果手机复制粘贴内容弹窗提醒功能的方法如下…

Java-API简析_java.util.Objects类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/133463511 出自【进步*于辰的博客】 因为我发现目前&#xff0c;我对Java-API的学习意识比较薄弱…

Linux Ubuntu配置Git的方法

本文介绍在Linux操作系统的Ubuntu版本中&#xff0c;配置分布式开源版本控制系统Git&#xff0c;随后基于Git克隆GitHub中项目的代码的详细方法。 在之前的文章分布式版本控制系统Git的下载、安装与使用其复制GitHub项目代码的方法&#xff08;https://blog.csdn.net/zhebushib…

【前段基础入门之】=>你不知道的 CSS 选择器的进阶使用!

导语&#xff1a; 在上一章节中&#xff0c;我们了解了 CSS 的一些基本语法概念&#xff0c;那么在这一章节中我们就带来 CSS 选择器知识的分享&#xff0c;选择器这一章的知识点有一点多&#xff0c;不过我们只要认真去理解&#xff0c;学习它也是没什么问题的&#xff0c;还有…

【模型压缩】Distiller学习-初认识

Distiller学习-初认识 简介 Intel AILab的神经网络压缩框架&#xff0c;建立在Pytorch基础上 安装 压缩方法 权重正则化方法权重剪枝方法训练后量化方法训练时量化方法条件计算低质分解方法知识蒸馏方法 总体目录 核心代码实现 所有案例的配置文件 举例 初始化网络评价网络…

基于Vue和Element UI实现前后端分离和交互

目录 前言 一、Element UI简介 1.Element UI是什么 2.Element UI的特点 二、项目搭建 1.创建一个SPA项目 2.安装 Element-UI 3.导入组件 4.创建登陆注册界面 登录组件---Login.vue 注册组件---Register.vue 定义组件与路由的对应关系 效果演示&#xff1a; 三、前…

1.4.C++项目:仿muduo库实现并发服务器之buffer模块的设计

项目完整版在&#xff1a; 一、buffer模块&#xff1a; 缓冲区模块 Buffer模块是一个缓冲区模块&#xff0c;用于实现通信中用户态的接收缓冲区和发送缓冲区功能。 二、提供的功能 存储数据&#xff0c;取出数据 三、实现思想 1.实现换出去得有一块内存空间&#xff0c;采…

Redis与分布式-集群搭建

接上文 Redis与分布式-哨兵模式 1. 集群搭建 搭建简单的redis集群&#xff0c;创建6个配置&#xff0c;开启集群模式&#xff0c;将之前配置过的redis删除&#xff0c;重新复制6份 针对主节点redis 1&#xff0c;redis 2&#xff0c;redis 3都是以上修改内容&#xff0c;只是…

十、空闲任务及其钩子函数

1、空闲任务的介绍 (1)一个良好的程序&#xff0c;它的任务都是事件驱动的&#xff1a;平时大部分时间处于阻塞状态。 (2)有可能我们自己创建的所有任务都无法执行&#xff0c;但是调度器必须能找到一个可以运行的任务。所以&#xff0c;我们要提供空闲任务。 (3)在使用vTas…

格拉姆角场GAF将时序数据转换为图像并应用于凯斯西楚大学轴承故障诊断(Python代码,CNN模型)

1.运行效果&#xff1a; 格拉姆角场GAF将时序数据转换为图像并应用于故障诊断&#xff08;Python代码&#xff09;_哔哩哔哩_bilibili 环境库 只要tensorflow版本大于等于2.4.0即可运行 2.GAF的内容 GAF是一种用于时间序列数据可视化和特征提取的技术&#xff0c;通常用于…

Linux——补充点(进程切换及页表映射)

目录 补充点1&#xff1a;进程地址空间堆区管理 补充点2&#xff1a;Linux内核进程上下文切换 补充点3&#xff1a;页表映射 补充点4&#xff1a;两级页表 补充点1&#xff1a;进程地址空间堆区管理 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程&#…

keil调试的时候没问题,下载时候没反应

今天遇到这样一个问题。我下载商家的代码例程后单片机没反应&#xff0c;进入调试的时候一切正常。很奇怪&#xff0c;在网上找了教程问题解决&#xff0c;总结一下。 原因在于程序下载进去后没有按下复位键&#xff0c;导致还是之前的程序。我之前设置的是下载后自动复位运行…

【STL】用一棵红黑树封装map和set

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C进阶 ⭐代码仓库&#xff1a;C进阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

玩转gpgpu-sim 04记—— __cudaRegisterBinary() of gpgpu-sim 到底做了什么

官方文档&#xff1a; GPGPU-Sim 3.x Manual __cudaRegisterBinary(void*) 被执行到的代码逻辑如下&#xff1a; void** CUDARTAPI __cudaRegisterFatBinary( void *fatCubin ) { #if (CUDART_VERSION < 2010)printf("GPGPU-Sim PTX: ERROR ** this version of GPGPU…

小程序如何设置余额充值

在小程序中设置余额充值是一种非常有效的方式&#xff0c;可以帮助商家吸引更多的会员并提高用户的消费频率。下面将介绍如何在小程序中设置余额充值并使用。 第一步&#xff1a;创建充值方案 在小程序管理员后台->营销管理->余额充值页面&#xff0c;添加充值方案。可…

Spark性能监测+集群配置

spark-dashboard 参考链接 架构图 Spark官网中提供了一系列的接口可以查看任务运行时的各种指标 运行 卸载docker https://blog.csdn.net/wangerrong/article/details/126750198 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest…