向上调整建堆与向下调整建堆的时间复杂度 AND TopK问题

news2024/11/20 13:30:05

目录

  • 前言
  • 建堆的时间复杂度
  • TOPK问题
  • 总结

前言

本篇旨在介绍使用向上调整建堆与向下调整建堆的时间复杂度. 以及topk问题

博客主页: 酷酷学!!!
感谢关注~

建堆的时间复杂度

堆排序是一种优于冒泡排序的算法, 那么在进行堆排序之前, 我们需要先创建堆, 为什么说堆排序的是优于冒泡排序的呢? 那么这个建堆的时间复杂度是多少呢?

void HeapSort(int* a, int n)
{
	//降序
	//创建小堆
	//向下调整创建,从最有一个非叶子节点
	//时间复杂度O(N)
	for (int i = (n-1-1)/2; i>=0; i--)
	{
		Adjustdown(a, n, i);
	}
	//堆创建之后,交换第一个节点与最后一个节点,
	//时间复杂度为O(N*logN)
	int end = n-1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		Adjustdown(a, end, 0);
		end--;
	}
}

首先来看向下调整算法建堆的时间复杂度, 因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个结点不影响最终结果):

假设高度为h的二叉树, 结点的个数为N, 可以计算出高度h与结点个数之间的关系如下图所示:

在这里插入图片描述

向下调整算法, 从最后一个非叶子结点开始向下调整, 也就是第h-1层, 需要向下移动一层, 第h-2层需要向下移动2层, … , 第一层则需要向下移动h-1层, 第二层的结点需要向下移动h-2层. 依次类推, 如图所示.
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
可以看出节点数量多的层调整次数少, 结点数量少的层调整次数多 . 错位相减法则可以计算出T(N) = 2^h - 1 - h, 带入h与N的关系则得出向下调整建堆的时间复杂度为O(N).

void Heapsort(int* a,int n)
{
	//时间复杂度为O(N*logN)
	for (int i = 1; i < n; i++)
	{
		AdjustUP(a, i);
	}
	//时间复杂度为O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

使用向上调整建堆, 计算其时间复杂度, 如下

在这里插入图片描述
总计调整次数为
在这里插入图片描述
使用错位相减法计算:

在这里插入图片描述
可以看出结点数多的层, 调整次数也多, 结点数少的层, 调整次数少, 时间复杂度为O(N*logN), 所以一般建堆都采用向下调整建堆法.

TOPK问题

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

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

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

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

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

例如: 求十万个数据中最大的前K个数, 要求只有1kb内存, 这些数据存储在磁盘中

首先先用前k个数建一个小堆, 剩下的N-K个元素依次与堆顶元素进行比较, 如果大于堆顶元素, 则替换堆顶元素, 并且向下调整堆, 结束后, 堆中数据即最大的k个元素.

代码如下:

第一步先使用随机数生成十万个数据

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) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

读取数据, 并且用前k个数据创建一个小堆, 然后读取剩下的数据, 如果比堆顶数据大, 就替代堆顶数据进入堆, 然后向下调整堆.

void TestHeap()
{
	int k;
	printf("请输入k>:");
	scanf("%d", &k);
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc fail");
		return;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	// 读取文件中前k个数
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}
	// 建K个数的小堆
	for (int i = (k-1-1)/2; i>=0 ; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	// 读取剩下的N-K个数
	int x = 0;
	while (fscanf(fout, "%d", &x) > 0)
	{
		if (x > kminheap[0])
		{
			kminheap[0] = x;
			AdjustDown(kminheap, k, 0);
		}
	}

	printf("最大前%d个数:", k);
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}
int main()
{
	CreateNDate();

	TestHeap();

	return 0;
}

总结

建堆的时间复杂度为O(N), 使用堆排序的时间复杂度为O(N*logN), 而使用冒泡排序的时间复杂度为O(N^2), 故堆排序的效率明显高于冒泡排序, 而topk则解决了使用较小内存而求取一堆数据中最大或者最小的前k个数据.

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

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

相关文章

网络模型-VLAN聚合

VLAN聚合 VLAN聚合(VLAN Aggregation,也称SuperVLAN)指在一个物理网络内&#xff0c;用多个VLAN(称为Sub-VLAN)隔离广播域并将这些Sub-VLAN聚合成一个逻辑的VLAN(称为SuperVLAN)&#xff0c;这些Sub-VLAN使用同一个IP子网和缺省网关&#xff0c;&#xff0c;进而达到节约IP地址…

leetcode_2024年5月19日10:51:26

238.除自身以外各元素的乘积 给你一个整数数组nums&#xff0c;返回数组answer&#xff0c;其中answer[i]等于nums中除nums[i]之外其余各元素的乘积。 题目数据保证数组nums之中任意元素的全部前缀元素和后缀的乘积都在32位整数范围内。 请不要使用除法&#xff0c;且在o&am…

Go 切片常用操作与使用技巧

1.什么是切片 在 Go 语言中的切片&#xff08;slice&#xff09;是一种灵活的动态数组&#xff0c;它可以自动扩展和收缩&#xff0c;是 Go 语言中非常重要的数据结构之一。切片是基于数组实现的&#xff0c;它的底层是数组&#xff0c;可以理解为对底层数组的抽象。它会生成一…

智能车竞赛指南:从零到一,驶向自动驾驶的未来

智能车竞赛指南&#xff1a;从零到一&#xff0c;驶向自动驾驶的未来 一、智能车竞赛概览1.1 竞赛介绍1.2 竞赛分类 二、智能车开发技术基础2.1 硬件平台2.2 软件开发 三、实战案例&#xff1a;循线小车开发3.1 系统架构3.2 代码示例 四、技术项目&#xff1a;基于ROS的视觉导航…

一款功能强大的安卓虚拟机应用——VMOS Pro使用分享

前段时间我刚刚分享一个WeChat平板模块能够允许用户自由修改系统设置&#xff0c;让你的Android备用手机焕发新生&#xff0c;实现手机PAD化&#xff0c;实现两台设备同时登录微信号。今天我分享的这个相比WeChat更为简单&#xff0c;因为它可以通过虚拟机的方式进行多种androi…

【Numpy】深入解析numpy.diag()函数

numpy.diag()&#xff1a;深入探索NumPy库中的对角矩阵操作 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#x1f3…

MGRE实验——路由配置

对134环回 ping一下发现都可以通 配置3&#xff0c;4同3 再注册 然后内网要互通&#xff0c;起rip 宣告1的左边和右边 对3 对4 当3&#xff0c;4之间要互通时&#xff0c;首先在1上 关闭之后&#xff0c;3就能学到4上的用户网段&#xff0c;4也能学到3 局域网要访问广域网一定…

Web 3D 框架简介

前言 3D游戏引擎的历史可以追溯到20世纪80年代末和90年代初。当时,计算机技术迅速发展,人们开始对图形和游戏感兴趣。以下是3D游戏引擎的历史故事: 早期引擎的诞生(1980-1990年代) 在这个时期,一些早期的3D游戏引擎开始出现。其中一个著名的例子是id Software开发的Do…

单细胞分析(Signac): PBMC scATAC-seq 聚类

引言 在本教学指南中&#xff0c;我们将探讨由10x Genomics公司提供的人类外周血单核细胞&#xff08;PBMCs&#xff09;的单细胞ATAC-seq数据集。 加载包 首先加载 Signac、Seurat 和我们将用于分析人类数据的其他一些包。 if (!requireNamespace("EnsDb.Hsapiens.v75&qu…

Dijkstra算法在《庆余年》中的应用:范闲的皇宫之旅

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

英语学习笔记24——Give me/us/him/her/them some ...

Give me/us/him/her/them some … 给我/我们/他/她/他们一些…… 词汇 Vocabulary desk n. 课桌&#xff08;有书桌堂&#xff09;&#xff0c;写字台 复数&#xff1a;desks 搭配&#xff1a;desk mate 同桌    构成&#xff1a;desk mate 桌子上的伙伴 同桌    cl…

FBB-Frontiers in Bioengineering and Biotechnology

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、投稿咨询 一、期刊简介 Frontiers in Bioengineering and Biotechnology是专注生物工程和生物技术领域的开放获取期刊。 研究范围涵盖生物材料、生物力学、生物工艺工程、生物安全和生物安保&#xff0c;生物传…

计算机系统基础 7 分支程序的实现

简单条件转移指令 根据单个标志位的值&#xff08;CF&#xff0c; SF&#xff0c;OF&#xff0c;PF&#xff0c;ZF&#xff09;来确定是否转移&#xff0c; 如果条件成立&#xff0c;则&#xff08;EIP&#xff09; 位移量 ➡ EIP&#xff0c;否则什么也不做。 注意&#xff0…

【Andoird开发】android获取蓝牙权限,搜索蓝牙设备MAC

<!-- Android 12以下才需要定位权限&#xff0c; Android 9以下官方建议申请ACCESS_COARSE_LOCATION --><uses-permission android:name"android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name"android.permission.ACCES…

参考文献交叉引用两个文献,逗号隔开

1.引用两个参考文献&#xff0c;定位到word正文中需要引用的位置&#xff0c;然后插入-交叉引用&#xff0c;引好文献 2.选中两个参考文献&#xff0c;切换域代码&#xff0c;然后进行修改&#xff1a; 改为 上面的两张图片中的点是空格的含义&#xff0c;word中按ctrlshift8就…

【计算机网络实验】TCP协议的抓包分析:三次握手四次挥手UDP和TCP的区别(超详细教程)

计算机网络实验——TCP协议抓包分析 文章目录 计算机网络实验——TCP协议抓包分析一、基础知识点1、运输层两个重要协议的特点对比&#xff08;TCP和UDP&#xff09;2、TCP报文的格式3、常见的TCP报文标识字段&#xff08;FLAG字段&#xff09;4、TCP连接的建立过程及理解——三…

CTFshow之文件上传web入门151关-161关解密。包教包会!!!!

这段时间一直在搞文件上传相关的知识&#xff0c;正好把ctf的题目做做写写给自字做个总结&#xff01; 不过有一个确定就是所有的测试全部是黑盒测试&#xff0c;无法从代码层面和大家解释&#xff0c;我找个时间把upload-labs靶场做一做给大家讲讲白盒的代码审计 一、实验准…

多台Centos快速区分,让Centos开机自动显示它的IP地址!

背景说明&#xff1a;当公司拥有多台Centos服务器&#xff0c;管理员很容易弄混淆导致不好区分&#xff0c;在这样的情况下我们可以写个简单脚本来实现开机自动显示它的IP地址&#xff0c;从而达到区分开来的结果&#xff01; 首先我们来开下效果&#xff0c;登录之前的 下面是…

调试时JSON库一直提示 PDB找不到 使用需要对象文件来进行调试的 /DEBUG:Fastlink生成的

最近调试时一直提示上面的提示框&#xff0c;很是烦躁。 为什么会出现这个错误呢&#xff0c;我一直使用的是/DEBUG。出现原因没有找出来&#xff0c;理论上市使用了/DEBUG:Fastlink这个模式才会出&#xff0c;但是就是一直在报这个错误。 /DEBUG&#xff08;生成调试信息&am…

Linux进程的地址空间

Linux进程的地址空间 1. 前言 在编写程序语言的代码时&#xff0c;打印输出一个变量的地址时&#xff0c;这个地址在内存中是以什么形式存在的&#xff1f;一个地址可以存储两个不同的值吗&#xff1f; 运行以下代码&#xff1a; #include <stdio.h> #include <un…