如何在10亿数中找出前1000大/小的数?

本文介绍了在内存空间充足和有限的情况下,如何高效找出10亿数中的前1000大/小的数。当内存空间足够时,采用分治法,通过递归分割数组,实现O(N)的时间复杂度。若内存有限,可使用分布式分治法,将数据分散到多台机器处理,最后合并结果,或采用建堆法,维护一个大小为1000的小顶堆,达到O(N·logN)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、内存空间足够大

分治法

随机选一个数flag,然后对整个数组进行分割,会得到两部分,前一部分的数都大于flag,后一部分的数都小于flag。

示例:

在这里插入图片描述

如果说前一部分总数大于1000个,那就继续在前一部分进行分割。如果前一部分的数小于1000个,那就在后一部分再进行分割,寻找剩下的数。利用分治法,对左边或者右边进行循环分割,直到找够Top 1000。

时间复杂度 :O(N)

计算过程

我们在进行第一次分割的时候需要花费N,第二次分割的时候,数据量减半了,所以只要花费N/2,同理第三次的时候只要花费N/4,以此类推。而N+N/2+N/4+…取最高指数项,去掉系数,所以这个方法的渐进时间只有O(N)

二、内存空间有限

分布式分治法

利用分布式的思想,将数据切分,然后在多台机器上分别计算Top N,然后将这些数汇总再计算Top N。

示例:

在这里插入图片描述

建堆法

PS:如果是求max Top N,那么在内存中维护一个小顶堆;如果是求min Top N,那么在内存中维护一个大顶堆。这里以max Top N示例。

原理

小顶堆的堆顶元素是整个堆中最小的,后续数据如果比堆顶还小,那么由于堆中本来就有N个数据,那么自然就不符合条件,可以丢弃了;如果后续数据比堆顶大,那么就说明这个数可能在Top N中,起码当前堆顶元素肯定就不在Top N中了,所以用这个数替换掉当前堆顶,由于这个数不一定是整个堆中最小的,所以还要调整堆,使堆继续保持堆的特性,以便继续判断后面的数据。这个过程遍历完了数据以后,堆中的元素就是要找的Top N了。

具体做法

先读取前N个数,在内存中创建一个小顶堆。

然后从第N+1个数开始,依次读取后面的数和堆顶数据进行比较,如果堆顶数,那么直接丢弃,否则用这个数替换掉当前的堆顶,然后调整堆,使之保持堆的特性。循环这个过程,遍历完后续数时,此时堆中的数就是max Top N。

示例

在这里插入图片描述

时间复杂度 :O(N·logN)

计算过程

​ 建堆的时间复杂度:O(N)

​ 调整堆的时间复杂度:O(N · logN)

​ 所以,总时间复杂度是:O(N·logN)

示例代码

//////////////////////////////////////////////////////////////
//@author 分时天月 on 2019/6/1
//////////////////////////////////////////////////////////////

#include <iostream>
#include <vector>
using namespace std;

class TopN{
   
   
public:
	void FindTopN(size_t n, vector<int>& data){
   
   
		//用前n个数建一个小顶堆
		BuildHeap(n, data);
		//用第n个值后面的数据调整堆
		for (size_t i = n; i != data.size(); ++i){
   
   
			adjust
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值