经典排序之topK问题

经典排序之topK问题

现代大数据环境下。对于大数据的处理很平常。

场景还原

对于给定的1亿或者无上限的数据。排序出top 50的数据。

问题分析

  • 如此大的数据让其全部加载到内存中再次排序肯定是不行的。内存不够啊
  • 再资源有限的情况下,使用分布式计算也是很有限的,因为小公司基本没可能

方案分析

对此有人给出了一个解决方案:堆排序。

  • 堆树有一个很大的特点,堆顶要么最大,要么最小。
  • 新增一个堆顶元素可以快速的重新堆化
  • 对于大数据量我们可以每次加载一定的数据而不用全部加载

代码再现

public class ObjectTopK<E> {

	//要排序的数量
    private int k;
    //数据存储
    private List<E> list;
    //比较器
    private Comparator comparator;
    //是否初始化堆
    private boolean init=false;
    
    public ObjectTopK(int k){
		this.k = k;
		//初始化大小
		list = new ArrayList<E>(k);
	}
	
	public ObjectTopK(int k,Comparator<? super E> comparator){
		this.k = k;
		//初始化大小
		list = new ArrayList<E>(k);
		this.comparator = comparator;
	}
	
	/**
	 * 添加元素
	 * @param e
	 */
	public void add(E e) {
		if( list.size() < k ) {
			list.add(e);
		}else if( !init ){
			buildMinHeap();
			init=true;
		}else {
			if( comparator != null && comparator.compare(e, list.get(0)) > 0) {
				list.set(0, e);
				buildMinHeap(0, list.size());
			}else if(((Comparable)e).compareTo((Comparable)list.get(0)) > 0) {
				list.set(0, e);
				buildMinHeap(0, list.size());
			}
			
		}
	}
	/**
	 * 构造最小堆
	 */
	private void buildMinHeap(){
		for(int i=list.size()/2 -1 ; i>=0 ; i--) {
			buildMinHeap(i, list.size());
//			print();
//			System.out.println();
		}
	}
	
	/**
	 * 堆化
	 */
	private void buildMinHeap(int start, int end) {
		if( comparator != null ) {
			int left = 2*start+1;
			while(left < end) {
				int min = left;
				if( left+1 < end && comparator.compare(list.get(left+1), list.get(left))<0){
					min = left+1;
				}
				if(comparator.compare(list.get(start), list.get(min))<0) {
					break;
				} else {
					E temp = list.get(min);
					list.set(min, list.get(start));
					list.set(start, temp);
					start = min;
					left = 2*start + 1;
				}
			}
		}else if( list.get(0) instanceof Comparable){
			int left = 2*start+1;
			while(left < end) {
				int min = left;
				if( left+1 < end && ((Comparable)list.get(left+1)).compareTo((Comparable)list.get(left)) < 0){
					min = left+1;
				}
				if( ((Comparable)list.get(start)).compareTo((Comparable)list.get(min)) < 0 ) {
					break;
				} else {
					E temp = list.get(min);
					list.set(min, list.get(start));
					list.set(start, temp);
					start = min;
					left = 2*start + 1;
				}
			}
		} else {
			throw new RuntimeException("数据不能比较");
		}
	}
	
	/**
	 * 堆排序
	 */
	public void sort() {
		for(int i=list.size()-1; i>=0; i--) {
			E temp = list.get(0);
			list.set(0, list.get(i));
			list.set(i, temp);
			buildMinHeap(0, i);
		}
	}
	
	/**
	 * 输出结果
	 */
	public void print() {
		for(E e: list) {
			System.out.print(e+" ");
		}
	}
	public static void main(String[] args) throws IOException {
		ObjectTopK<Integer> obj = new ObjectTopK<Integer>(50);
		Random random = new Random();
		long start = System.currentTimeMillis();
		for(int i=0;i<Integer.MAX_VALUE;i++) {
			obj.add(random.nextInt(Integer.MAX_VALUE));
		}
		obj.sort();
		obj.print();
		System.out.println("=============================");
		System.out.println("21亿数据耗时:"+(System.currentTimeMillis()-start)/1000);
	}
	
}

执行结果

2147483644 2147483643 2147483641 2147483639 2147483637 2147483637 2147483636 2147483633 2147483633 2147483632 2147483630 2147483629 2147483627 2147483627 2147483627 2147483626 2147483625 2147483625 2147483625 2147483619 2147483618 2147483618 2147483617 2147483612 2147483611 2147483611 2147483611 2147483609 2147483608 2147483607 2147483606 2147483606 2147483606 2147483605 2147483603 2147483602 2147483602 2147483599 2147483598 2147483597 2147483597 2147483597 2147483597 2147483592 2147483592 2147483590 2147483589 2147483589 2147483588 2147483588 =============================
21亿数据耗时:37

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值