寻找TopN——在10亿数据中找到1000个最大的数

1 篇文章 0 订阅

目录

问题描述

如何从10亿数据中找到前1000大的数?

解法

针对该问题,给定一个数组data,从中找出前n个最大的数。
解题思路
先维护一个具有n个数的堆,然后调整该堆为小顶堆,即每个父节点都比其子节点小。然后从剩下的数组中逐一读取数据,将读取到的数据跟堆顶比较。如果该数比堆顶小,直接丢弃;如果该数比堆顶数大,则用该数替换堆顶,重新调整该堆为小顶堆。等待所有数据处理完毕,这时候已经的小顶堆就是TopN。


public class TopN {
//	当前节点的父节点
	private int parent(int n){
		return (n-1)/2;
	}
//	当前节点的左子节点
	private int left(int n){
		return 2*n+1;
	}
//	当前节点的右子节点
	private int right(int n){
		return 2*n+2;
	}
//	构建堆
	private void buildHeap(int n, int[] data){
		for(int i=1;i<n;i++){
			int t=i;
			while(t!=0 && data[parent(t)]>data[t]){
				int temp = data[parent(t)];
				data[parent(t)]=data[t];
				data[t]=temp;
				t=parent(t);
			}
		}
	}
//	调整堆,为小顶堆
	private void adjust(int i, int n, int[] data){
		if(data[0]>=data[i]){
			return;
		}
		int temp = data[i];
	    data[i] = data[0];
	    data[0] = temp;
		int t=0;
//		调整时,堆顶比子节点大,从较小的子节点开始调整
		while((left(t)<n&&data[t]>data[left(t)])||(right(t)<n&&data[t]>data[right(t)])){
			if((right(t) < n && data[right(t)] < data[left(t)])){
				temp=data[t];
				data[t]=data[right(t)];
				data[right(t)]=temp;
				t=right(t);
			}else{
				temp=data[t];
				data[t]=data[left(t)];
				data[left(t)]=temp;
				t=left(t);
			}
		}
	}
//	寻找topN数
	public void findTopN(int n, int[] data){
		buildHeap(n,data);
		for(int i=n;i<data.length;i++){
			adjust(i,n,data);
		}
	}
//	打印
	public void print(int[] data){
		for(int i=0;i<data.length;i++){
			System.out.print(data[i]+",");
		}
	}
}

补充

这里寻找TopN的问题,也可以用分治法,类似快排。

随机选一个数t,对数组进行partition,这时数组一分为二,前一部分大于t,后一部分小于t。如果前一部分数据个数等于1000,直接结束;如果前一部分数据个数大于1000,那么在前一部分中继续partition;如果前一部分数据个数小于1000,那么在后一部分中partition。时间复杂度是o(n),但10亿数据,占大量内存;改进:将数据写入文件中,但多次对磁盘读取,影响效率;再改进:采用分布式的思想。

在我看来,更好的是正文中介绍的方法,维护一个N个数的堆。

本文学习的是https://mp.weixin.qq.com/s/V-VhsE-5aYU3v8RA8mCgaQ

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值