数据结构第十二弹---堆的应用

堆的应用

  • 1、堆排序
  • 2、TopK问题
  • 3、堆的相关习题
  • 总结

1、堆排序

要学习堆排序,首先要学习堆的向下调整算法,因为要用堆排序,你首先得建堆,而建堆需要执行多次堆的向下调整算法。
但是,使用向下调整算法需要满足一个前提:
 若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
 若想将其调整为大堆,那么根结点的左右子树必须都为大堆。

在这里插入图片描述

向下调整算法的基本思想(大堆):
 1.从根结点处开始,选出左右孩子中值较大的孩子。
 2.让大的孩子与其父亲进行比较。  若大的孩子比父亲还大,则该孩子与其父亲的位置进行交换。并将原来大的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。
 若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。

在这里插入图片描述
使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆呢?

答案很简单,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。

在这里插入图片描述
建堆代码

//建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, php->size, i);
	}

根据上一弹堆的向下调整算法时间复杂度计算可知,建堆的时间复杂度为O(N)。
那么堆建好后,如何进行堆排序呢?
步骤如下:

1、将堆顶数据与堆的最后一个数据交换,然后对根位置进行一次堆的向下调整,但是调整时被交换到最后的那个最大的数不参与向下调整。
2、完成步骤1后,这棵树除最后一个数之外,其余数又成一个大堆,然后又将堆顶数据与堆的最后一个数据交换,这样一来,第二大的数就被放到了倒数第二个位置上,然后该数又不参与堆的向下调整…反复执行下去,直到堆中只有一个数据时便结束。此时该序列就是一个升序。

为什么升序用到的是大堆呢?

大堆的堆顶是最大的数,可以将其放在数组尾,然后再通过向下调整算法找到次大的数。而小堆的堆顶是最小的数,需要放在第一个位置,如果用原来的堆找不到次小的数,而重新建堆则会更加繁琐。

堆排序实现

//升序 大堆
void HeapSort(int arr[], int size)
{
	assert(arr);
	//1.建堆 向上调整 O(N*logN)
	//for (int i = 1; i < size; i++)
	//{
	//	AdjustUp(arr, i);
	//}
	
	//1.向下调整建堆 O(N)
	//从第一个非叶子结点开始向下调整,一直到根
	for (int i = (size - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, size, i);
	}
	//2.向下调整 O(N*logN)
	int end = size - 1;//记录堆的最后一个数据的下标
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);//将堆顶的数据和堆的最后一个数据交换
		AdjustDown(arr, end, 0);//对根进行一次向下调整
		end--;//堆的最后一个数据的下标减一
	}
}

2、TopK问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能
数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

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

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

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

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

假设我们要找出10亿个随机数中的前k个最大的数。
假设数据类型为int,那么占用的内存是多少?
1GB=1024MB
1MB=1024Kb
1KB=1024Byte
10亿个数则是10亿字节,40亿Byte=3,906,250KB=3,814.697MB=3.725GB
因为内存的空间是有限的,所以在处理这么大内存的数据时,我们需要用到文件处理。

为了让速度较快一些,我们就假设在一千万个随机数中求前K个数。

1、我们需要创建一个有一万个数的文件。

此处我们需要用到两个文件处理函数和文件打开关闭函数。

//文件打开
FILE * fopen ( const char * filename, const char * mode );//前面参数为文件名 后面参数为文件打开方式
//文件关闭
int fclose ( FILE * stream );
int fprintf ( FILE * stream, const char * format, ... );//将后面函数写入的信息写入stream
int fscanf ( FILE * stream, const char * format, ... );//将stream的信息写入后面的函数

创建随机值,需要用到srand(),但是随机数的范围为0-32767。最多只有32768个,远小于一千万,为了减少随机输的重复,我们需要将随机数加上一个数。单纯的使用srand不是真正的随机时,这些我们需要用到时间这个参数,使它为真正的随机数。srand的头文件是#include<stdlib.h>。time的头文件是#include<time.h>。

srand((unsigned int)time(NULL));

代码实现

//1.创造随机数到文件中
void CreateNDate()
{
	int n = 10000000;
	srand((unsigned int)time(NULL));
	FILE* pf = fopen("data.txt", "w");//以写的方式打开文件
	if (pf == NULL)
	{
		perror("fopen fail");
		return;
	}
	for (int i = 0; i < n; i++)
	{
		//rand随机数有限制 只有几万个 所以+i
		int x = (rand() + i) % 10000000;
		fprintf(pf, "%d\n", x);//将数据写入文件中
	}
	fclose(pf);//关闭文件
	pf = NULL;//手动置空
}

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

2、 用数据集合中前K个元素来建堆,用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

此处找最大的前k个,建小堆。
建小堆大的数据才能进来,最后留下的也是大的数据。
建大堆则只能进来小的数据,不符合题意。

2.1、打开文件

//打开文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
	perror("fopen error");
	return;
}

2.2、读取文件并将前k个数据创建小堆

int* minheap = (int*)malloc(sizeof(int) * k);
if (minheap == NULL)
{
	perror("malloc fail");
	return;
}
//1.读取文件前k个建小堆 
for (int i = 0; i < k; i++)
{
	fscanf(pf, "%d", &minheap[i]);
	AdjustUp(minheap, i);
}

2.3、用剩余的N-K个元素依次与堆顶元素来比较

//2.读取文件的数据与堆顶数据进行比较 如果大则 向下调整
int x = 0;
while (fscanf(pf, "%d", &x) != EOF)
{
	if (x > minheap[0])
	{
		minheap[0] = x;
		AdjustDown(minheap, k, 0);
	}
}

2.4、将前k个数据打印出来并关闭文件

for (int i = 0; i < k; i++)
{
	printf("%d ", minheap[i]);
}
free(minheap);
fclose(pf);
pf = NULL;

测试
在这里插入图片描述
虽然我们打印出了前k大值,那我们怎么知道这个算法就是对的呢?

此处博主的解决办法是修改文件中的k个值,改为都是超过一千万的值,如果打印出来的K个值是修改的值,那么此算法就是正确的。

在这里插入图片描述
经过修改打印出来的就是修改的值,说明算法没有问题。此处如果需要升序或者降序打印,进行一个排序算法即可。

3、堆的相关习题

1.下列关键字序列为堆的是:()
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

在这里插入图片描述

2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次
数是()。
A 1
B 2
C 3
D 4

在这里插入图片描述

3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为
A(11 5 7 2 3 17)
B(11 5 7 2 17 3)
C(17 11 7 2 3 5)
D(17 11 7 5 3 2)
E(17 7 11 3 5 2)
F(17 7 11 3 2 5)

在这里插入图片描述

4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
A[3,2,5,7,4,6,8]
B[2,3,5,7,4,6,8]
C[2,3,4,5,7,8,6]
D[2,3,4,5,6,7,8]

在这里插入图片描述

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

全网最细RocketMQ源码一:NameSrv

一、入口 NameServer的启动源码在NameStartup&#xff0c;现在开始debug之旅 二、createNamesrcController public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {System.setProperty(RemotingCommand.REMOTING_VER…

Java中多线程二

抢占调度模型 概述&#xff1a;优先让优先级高的线程使用 CPU &#xff0c;如果线程的优先级相同&#xff0c;那么随机会选择一个&#xff0c;优先级高的线程获取的 CPU 时间片相对多一些 Thread 类中一些关于线程的方法 方法简述public final int getPriority()返回此线程的优…

五、Java中SpringBoot组件集成接入【slf4j日志文档】

五、Java中SpringBoot组件集成接入【slf4j日志文档】 1.slf4j简介2.maven依赖3.配置4.使用5.展示6.参考文章 1.slf4j简介 SLF4J&#xff08;Simple Logging Facade for Java&#xff09;是一个为Java应用程序提供统一日志接口的日志门面框架。它旨在解决Java应用程序中日志系统…

居中面试问题

前端常问居中面试问题 css文本居中 文本水平居中 <div class"father"><div class"child"><div> <div>子类元素为行内元素&#xff0c;则给父类元素定义text-align:center 如果子元素是块元素&#xff0c;则给子元素定义margin&…

Vue3快速入门

文章目录 1. Vue3简介1.1. 【性能的提升】1.2. 源码的升级】1.3. 【拥抱TypeScript】1.4. 【新的特性】 2. 创建Vue3工程2.1. 【基于 vue-cli 创建】2.2. 【基于 vite 创建】(推荐)2.3. 【一个简单的效果】 3. Vue3核心语法3.1. 【OptionsAPI 与 CompositionAPI】Options API 的…

Linux系统——测试端口连通性方法

目录 一、TCP端口连通性测试 1、ssh 2、telnet&#xff08;可能需要安装&#xff09; 3、curl 4、tcping&#xff08;需要安装&#xff09; 5、nc&#xff08;需要安装&#xff09; 6、nmap&#xff08;需要安装&#xff09; 二、UDP端口连通性测试 1、nc&#xff08;…

85.乐理基础-记号篇-速度记号

内容来源于&#xff1a;三分钟音乐社 上一个内容&#xff1a;85.乐理基础-记号篇-力度记号-CSDN博客 速度记号在下方两个里面已经写过一部分了&#xff0c;这些标记总体上是属于 不变速度 的标记&#xff0c;比如一首乐谱就记了 每分钟60拍&#xff0c;那整首速度就都是不变的…

org.springframework.web.servlet.HandlerInterceptor

过期 1 配置黑名单 2 启动注册拦截 3 浏览器访问拦截

【Spring Cloud】Sentinel流量限流和熔断降级的讲解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Spring Cloud》。&#x1f3af;&#x1f3af; &am…

【SAP-PP】生产订单导入问题--完成日期向前推了一天

问题描述&#xff1a; 在执行BAPI_PRODORD_CREATE生产订单导入的时候&#xff0c;发现填写入模板中的基本完成日期是12月31日&#xff0c;但是到具体工单时变成了12月30日 截图说明&#xff1a; 感觉很神奇&#xff0c;咋一看&#xff0c;真的是日期提前了一天&#xff0c;de…

线性回归实例

1、线性回归&#xff08;linear Regression&#xff09;和逻辑回归&#xff08;logistic Regression&#xff09;的区别 线性回归主要是用来拟合数据&#xff0c;逻辑回归主要是用来区分数据&#xff0c;找到决策边界。 线性回归的代价函数常用平方误差函数&#xff0c;逻辑回…

函数指针和回调函数

文章目录 一.函数指针1.什么是函数指针2.函数指针的形式3.函数指针的用途。1.调用函数2.作为参数进行传递 二.函数指针数组三.回调函数 一.函数指针 1.什么是函数指针 函数指针是指向函数的指针。在C语言和C中&#xff0c;函数指针可以用来存储函数的地址&#xff0c;并且可以…

Kotlin程序设计(二)面向对象

Kotlin程序设计中级篇 我们在前面已经学习了Kotlin程序设计的基础篇&#xff0c;本章我们将继续介绍更多Kotlin特性&#xff0c;以及面向对象编程。 函数 其实函数我们在一开始就在使用了&#xff1a; fun main() {println("Hello World") }我们程序的入口点就是…

恒创科技:解决Windows服务器磁盘空间不足的问题

​  服务器硬盘的大小是决定空间是否充足的主要因素。但在日常使用中&#xff0c;服务器和网站备份会消耗大量存储空间&#xff0c;如果维护不当&#xff0c;最终将耗尽您的容量。同样&#xff0c;日志文件、临时文件和数据库可以在硬盘驱动器上或回收站中无休止地建立。当您…

windows10使用Shift+Win+S快捷键来截图

有时候电脑上没有打开微信QQ等软件&#xff0c;但是想使用一下截图&#xff0c;就很麻烦&#xff0c;还好windows10开始已经支持快捷键截图了&#xff1a; 打开截图工具并获取屏幕截图 - Microsoft 支持 快捷键&#xff1a;ShiftWinS 使用教程&#xff1a; 顺带说下系统这个自…

Mingw32编译opencv库

文章目录 1. 准备工作2. 编译cmake构建程序mingw32-make编译 3. 安装4. 安装完的结果 注意&#xff1a; mingw32-make编译的库和MSVC编译的库不兼容&#xff0c;MSVC和mingw-make生成的动态库使用的是不同的ABI&#xff08;Application Binary Interface&#xff09;&#xff0…

货拉拉智能监控实践:如何解决多云架构下的故障应急问题?

一分钟精华速览 在月活超千万的大规模业务背景下&#xff0c;货拉拉遭遇了多云环境下的监控碎片化、规划无序等问题。为了应对这些挑战&#xff0c;货拉拉开发了一站式监控平台——Monitor。该平台的部署有效地实现了对核心应用的监控和报警全覆盖&#xff0c;显著提高了应急响…

【算法Hot100系列】组合总和

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

【算法】七夕祭

题目 七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。 于是 TYVJ 今年举办了一次线下七夕祭。 Vani 同学今年成功邀请到了 cl 同学陪他来共度七夕&#xff0c;于是他们决定去 TYVJ 七夕祭游玩。 TYVJ 七夕祭和 11 区的夏祭的形式很像。 矩形的祭典会场由 N 排 M 列共…

作业--day45

定时播放 #include "mywidget.h" #include "ui_mywidget.h"MyWidget::MyWidget(QWidget *parent) :QWidget(parent),ui(new Ui::MyWidget) {ui->setupUi(this);ui->bg_lab->setPixmap(QPixmap(":/pictrue/shanChuan.jpg"));ui->bg_…