数据结构:树(2)【堆排序】【堆排序的时间复杂度】【topk】

news2024/11/16 13:56:32

一.堆排序

关于排序我们还是挺熟悉的,像是冒泡排序,快速排序等等。这里多介绍一种也挺牛的排序,叫做堆排序。在我的上一篇博客里我们了解到了关于堆的概念,堆又分为大堆和小堆。那么如果我们在进行堆排序的过程中,我们要如何选择大堆和小堆?

如果是升序,我们就用建大堆的方式,反之降序,建小堆。

堆排序的话我们用向下调整就可以完成全部的步骤了。

1.代码演示

直接就先把向下调整法放在这里。

void AdjustDown(HPDataType* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // child >= n说明孩子不存在,调整到叶子了
	{
		// 找出小的那个孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

有了向下调整的思路,我们就可以来写函数了。

void HeapSort(int* a, int n)
{
	// 降序,建小堆
	// 升序,建大堆

	for (int i = (n-1-1)/2; i >= 0; i--)//后面单独解释
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)//这个循环就是为了排序的
	{
		Swap(&a[0], &a[end]);//交换根和最后一个元素
		AdjustDown(a, end, 0);
		--end;
	}
}

这里面有两个问题需要解释一下,一个是为什么要降序建小堆,升序建大堆。还有一个是为什么上面代码for循环里的i等于(n-1-1)/2.

2.降序建小堆,升序建大堆

其实降序建小堆而不采用降序建大堆是为了更加省事一点。我来举个例子:

 这个我们看起来好像很简单的就把最大的找出来了,但是9的两个孩子节点是没有大小关系的,我们在排序完9之后,后面的节点的关系就全乱了,兄弟变成了父亲。我们必须要重新排序。

然而我们降序建小堆的话就不会出现这样的情况,我们会把根和最后一个节点调换位置,然后再进行向下调整的过程。这个跟堆的删除类似:

3.从最后一个节点的父亲节点开始调整 

之后的就是什么说的为什么上面代码for循环里的i等于(n-1-1)/2

其实这句话的意思就是从第一个非叶子节点开始向下调整。比如上图中的,如果要调整的话,我们就会从30这个数的位置开始调整。n代表堆里有多少个数据,n-1就是最后一个节点的下标,n-1-1除以2就是最后一个节点的父亲节点,然后在进行向下调整。

二.堆排序的时间复杂度

我们知道,关于一个堆,我们可以向上调整和向下调整,那么哪一种调整的方式更加好呢?

1.向下调整的时间复杂度

这里面需要用到高中的一些关于等比数列的知识。

因为树的高度为h,我们可以算出全部的节点个数n是多少,第一层是2^0,第二层是2^1,后面依次到最后一层2^(h-1)。等比数列相加最后得到的结果就是上图中的2^h-1。 

时间复杂度也就是O(n). 

2.向上调整的时间复杂度

很多人可能会认为向上调整的时间复杂度和向下调整的时间复杂度一样,实际上差别也是很大的。

对比一下两种调整方式就有很大的差别。列出来的等式也与上面的有很大差别:

这个依旧是通过等比数列的错位相减法来求的。计算方式与上面的一样。

T(N)=-N + (N+1)*(log(N+1)-1)+1 。

最后它的时间复杂度也就是O(N*logN)。

三.topk问题

topk问题就是说在N个数了找前k个大的数字。例如王者荣耀里的国服排名,就是典型的topk问题。知道了topk表达的是啥意思我们就可以想起简单的代码来实现这个问题。

比如直接将这N个数建立一个大的堆,最后逐个取堆顶元素。这个取堆顶元素其实就是上面我们所说的降序建小堆,升序建大堆,只不过少了一些循环而已。时间复杂度在建堆时是O(N),pop时是O(K*logN)。但是这种方法有一种致命缺陷,比如我们中国有十四亿人,我想要找出最富有的10个人,那么我们在建完这个堆时需要的内存将近4G。

对于大量的数据,如果我们取使用这种直接建堆的方式,是不是就不太合适了。那么还有一种方法,我们取前k个数建个小堆。堆顶的元素是这k个数中最小的数,每一次就跟下一个元素比较一下,如果比堆顶元素大,就把他替换掉,然后就向下调整就行了。

我们就在十万个数里找最大的前十个数。

下面是代码演示:

void CreatData()
{
	int n = 100000;//创建十万个数
	srand((unsigned int)time(NULL));//用时间函数创建变化的种子
	FILE* fin = fopen("data.txt", "w");//以写的形式打开文件
	if (fin == NULL)
	{
		perror("fin fopen");
		return;
	}
	for (int i = 0; i < n; i++)
	{
		int random = rand() + i;
		fprintf(fin, "%d\n", random);//往文件里写数
	}
	fclose(fin);//关闭文件
}
void Heaptopk()
{
	int k;
	printf("请输入k:\n");
	scanf_s("%d", &k);
	int* kminheap = (int*)malloc(sizeof(int) * k);//有多少个数就创建多少空间
	if (kminheap == NULL)
	{
		perror("malloc fail");
		return;
	}
	FILE* fout = fopen("data.txt", "r");//以读的形式打开文件
	for (int i = 0; i < k; i++)
	{
		fscanf_s(fout, "%d", &kminheap[i]);//把文件里的前k个数写进我们创建的那个数组
	}
	//建k个数的小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}
	//读取剩下的数
	int x = 0;
	while (fscanf_s(fout, "%d", &x) > 0)//成功读取到了数字进入循环
	{
		if (x > kminheap[0])
		{
			Swap(&x, &kminheap[0]);
			AdjustDown(kminheap, k, 0);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}
int main()
{
	CreatData();
	Heaptopk();
	return 0;
}

关于向下调整的方式大家可以看我的上一篇博客。

到这里本篇差不多就结束了,后面也会持续更新关于树的相关知识,感谢观看,如有错误还请多多指出。

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

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

相关文章

cs与msf权限传递,mimikatz抓取明文密码

目录 一、cs与msf权限传递 二、mimikatz抓取明文密码 一、cs与msf权限传递 cs传到msf&#xff1a; 创建foreign监听器-->msf监听模块设置端口-->cs执行新建会话选择创建的监听器 1.创建监听器&#xff1a; 2.msf监听设置端口&#xff1a; use exploit/multi/hander s…

计算机网络学习

文章目录 第一章信息时代的计算机网络因特网概述电路交换&#xff0c;分组交换&#xff0c;报文交换计算机网络的定义和分类计算机网络的性能指标常见的三种计算机网络体系计算机网络体系结构分层的必要性计算机网络体系结构分层思想举例计算机网络体系结构中的专用术语 第二章…

类别型特征

#机器学习 #深度学习 #基础知识 #特征工程 #数据编码 背景 在现实生活中,我们面对的数据类型有很多,其中有的数据天然为数值类型具备数值意义,那么可以很自然地和算法结合,但是大部分数据他没有天然的数值意义,那么将他们送入到算法前,就需要对数据进行编码处理,将其转换为数…

php -v在cmd中正常显示 在vscode中却报错

效果展示 原因 在vscode中 终端是 PowerShell PowerShell 默认情况下它不会继承系统的PATH环境变量 解决方案 使用CMD作为终端 打开VSCode设置&#xff08;File > Preferences > Settings 或 Ctrl,&#xff09;。搜索 terminal.integrated.shell.windows。更改其值…

vue3主题切换按钮与功能实现

代码: <template><div class"slideThree"><label class"theme-switch"><inputtype"checkbox"class"checkbox"v-model"isChecked"change"setTheme"id"slideThree"name"check…

光伏组件积灰检测系统

光伏组件积灰检测系统是一种专门用于监测光伏组件表面灰尘积累情况的设备。以下是关于该系统的详细信息和特点&#xff1a; 系统概述 光伏组件积灰检测系统安装在光伏板的框架上&#xff0c;通过实时监测光伏组件表面的灰尘厚度、分布情况和清洁度&#xff0c;为运维人员提供…

揭秘爬虫技术:从请求到存储的全方位解析

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、爬虫初探&#xff1a;请求与响应 二、数据解析&#xff1a;从混乱中提炼价值 三、数据…

CentOS 7如何使用systemctl管理应用

说明&#xff1a;本文介绍如何使用systemctl命令的方式来启动、查看、停止和重启应用&#xff0c;以安装后的prometheus、alertmanager为例&#xff1b; Step1&#xff1a;创建文件 在系统/etc/systemd/system/路径下&#xff0c;创建一个xxx.service文件&#xff0c;该文件内…

配置环境变量

配置环境变量$(xxxx)&#xff0c;代表宏 32位操作系统&#xff0c;请自觉将文中路径中所有的x64换成x86。 %符号表示引用系统环境变量或用户自定义的环境变量 如果你想将某个文件夹添加到Visual Studio的路径中&#xff0c;你可以在环境变量中添加%FolderName%&#xff0c;其…

使用阿里云OSS实现视频上传功能

目录 前言 视频上传 前言 阿里云对象存储服务&#xff08;OSS&#xff09;作为一种高可用、高扩展性的云端存储服务&#xff0c;为开发者提供了便捷、安全的对象存储解决方案。本文将介绍如何利用阿里云OSS实现视频上传功能。 视频上传 前期准备请看阿里云OSS文件上传和下载…

基于mybatis-plus的多语言扩展

概览 对于表中字段&#xff0c;需要实现多语言的方案探讨&#xff1a; 1.表中横向扩展多个字段分别存储中文&#xff0c;英文&#xff0c;俄语等语言字段&#xff0c;查询时&#xff0c;根据需要查询的语言&#xff0c;进行查询 2.增加一张多语言表&#xff0c;存储多语言信…

RedHat9网络配置设计

目录 一、实验目的 二、实验过程 1、配置新网络接口 2、多网卡配置网络 3、网络接口的绑定&#xff0c;进行远程访问 4、配置网络接口的组合 一、实验目的 本次实验的目的是使用nmcli命令工具配置网络&#xff0c;ens160配置多个网卡&#xff0c;进行网络接口的绑定与组合…

iPhone“已删除”照片被恢复,苹果到底有没有后门?

继微软本周推出的Windows“回忆”功能引发隐私焦虑&#xff0c;遭马斯克和安全大咖们猛烈抨击后&#xff0c;苹果iPhone手机近日也曝出了类似的“记忆门”。 删除十几年的iPhone照片被恢复 近日&#xff0c;有苹果手机用户更新了苹果上周发布的iOS 17.5系统后&#xff0c;意外…

ChatGPT越来越懒,都学会反过来PUA人类了

OpenAI 表示自 11 月 11 日以来&#xff0c;他们就没有更新过模型&#xff0c;模型行为是不可预测的&#xff0c;他们正在研究如何修复。 不知你有没有注意到&#xff0c;最近一段时间&#xff0c;GPT-4 变得有些「懒惰」&#xff0c;现在的它&#xff0c;老是拒绝执行某些任务…

关于亚马逊、速卖通、虾皮、Lazada等平台自养号测评IP的重要性

在自养号测评中&#xff0c;IP的纯净度是一个至关重要的问题&#xff0c;它直接关系到账号的安全性和稳定性如果使用了被平台识别为异常或存在风险的IP地址&#xff0c;那么账号可能会面临被封禁的风险。这将对账号的正常使用和测评过程中造成严重影响。而使用纯净的IP地址&…

oracle准确记录数据提交时间

注意&#xff1a;mysql中的默认值同样记录的是dml操作发出时的时间&#xff0c;并且没有找到mysql中准确记录commit时间的方法。 oracle中数据发生变动时&#xff0c;如何准确记录发生变动时的时间。一般会使用ts字段&#xff0c;该字段使用默认值&#xff0c;default to_char…

【JAVA WEB实用与优化技巧】如何自己封装一个自定义UI的Swagger组件,包含Swagger如何处理JWT无状态鉴权自动TOKEN获取

目录 一、Swagger 简介1. 什么是 Swagger&#xff1f;2. 如何使用 Swagger3. Springboot 中swagger的使用示例1. maven 引入安装2. java配置 二、Swagger UI存在的缺点1.不够方便直观2.请求的参数没有缓存3.不够美观4.如果是JWT 无状态登录&#xff0c;Swagger使用起来就没有那…

简化跨网文件传输摆渡过程,降低IT人员工作量

在当今数字化时代&#xff0c;IT企业面临着日益增长的数据交换需求。随着网络安全威胁的不断演变&#xff0c;网关隔离成为了保护企业内部网络不受外部威胁的重要手段。然而&#xff0c;隔离的同时&#xff0c;企业也需要在不同网络间安全、高效地传输文件&#xff0c;这就催生…

harmony开发遇到的问题

arkt-no-props-by-indexProperty ‘name’ has no initializer and is not definitely assigned in the constructor.arkts-no-any-unknown typescript 中的报错 Property ‘name’ has no initializer and is not definitely assigned in the construc… ArkTs编译常见错误汇…

全网爆火Remini 粘土滤镜风格,我用ComfyUI一键生成了(保姆级教程)!

一、火爆全网的Remini&#xff01; Remini真的火爆了&#xff01;最近大家的朋友应该都被粘土滤镜刷屏了。 小红书上粘土滤镜、粘土特效的帖子动不动就是几百万浏览量&#xff0c;几千赞。 在有些电商平台上还有人接单&#xff0c;帮忙定制remini粘土风格的照片&#xff01; …