算法:用Java实现在海量的数据中,找到最大的N个数字,也就是TopN问题

我准备用优先队列来实现TopN问题,海量数据是指计算机内存装不下的很大的数据文件,在里面找到最大的N个数字。因为是海量的数据文件,所以还是按照以前外部排序那种,每次读文件的一部分,然后一个一个地吐数字,以确保内存可以处理。然后在读文件吐出的一个个数字中,找到最大的N个数字

实现思路:

  1. 将海量的数据用文件流读入,可以一行一行地读,然后数字一个一个地吐出来
  2. 先吐出N个数字,建立优先队列,最小的数字优先级最大,可以用Java实现好了的优先队列
  3. 继续吐数字,如果吐出的数字小于等于优先队列的最小值,则放过
  4. 如果吐出的数字大于优先队列的最小值,则将优先队列最小的数字(优先级最大)出队,然后入队刚刚吐出来的数字。注意,优先队列里面数字的个数永远保持N个
  5. 所有的数字都吐完后,优先队列里面的N个数字就是海量数据中最大的N个数字

下面是我的实现代码,比如我们想要在10000个数字里面找到最大的20个数字,数字都是百万级的:

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Random;

/**
 * @author LiYang
 * @ClassName TopN
 * @Description 用优先队列实现TopN问题
 * @date 2019/11/13 11:05
 */
public class TopN {
    
    //需要得到的最大数字的数量
    //就是TopN中的N
    private int topNum;
    
    //用于实现TopN的优先队列
    private PriorityQueue<Long> priorityQueue;

    /**
     * TopN工具类的构造方法
     * @param topNum 需要统计的最大数字的个数
     */
    public TopN(int topNum) {
        this.topNum = topNum;
        this.priorityQueue = new PriorityQueue<>();
    }

    /**
     * 计算TopN的N个最大数字
     * @param element 海量数字之一
     */
    public void countTopN(long element) {
        //如果优先队列里面的数字已经有N个了
        if (priorityQueue.size() == topNum) {
            
            //查看优先队列中最小数字
            long minimum = priorityQueue.peek();
            
            //如果当前数字比优先队列中最小数字要大
            if (element > minimum) {
                
                //把优先队列中最小数字出队
                priorityQueue.poll();
                
                //让更大的数字入队
                priorityQueue.add(element);
            }
            
        //如果优先队列里面的数字还未到N个
        } else {
            //当前数字入队优先队列,并不做出队操作
            priorityQueue.add(element);
        }
    }

    /**
     * 在文件读完,countTopN操作结束后
     * 获取最大N个数字的数组,升序排列
     * @return TopN数字数组,升序
     */
    public long[] getTopN() {
        //TopN的结果数组
        long[] result = new long[topNum];

        //将TopN的数字全部出队
        for (int i = 0; i < result.length; i++) {
            //这样操作,结果数组就是升序的
            result[i] = priorityQueue.poll();
        }
        
        //返回TopN的结果数组
        return result;
    }

    /**
     * 测试TopN工具类
     * 测试用例是百万级的10000个数字,找出Top20的数字
     * @param args
     */
    public static void main(String[] args) {
        //模拟大文件上的海量数据
        long[] bigData = new long[10000];

        //随机数类
        Random random = new Random();
        
        //生成百万级的"大文件"上的数据
        for (int i = 0; i < bigData.length; i++) {
            bigData[i] = random.nextInt(10000000);
        }
        
        //创建TopN工具类的实例,统计最大的20个数字
        TopN topN = new TopN(20);

        //将"大文件"上的数据依次"吐出",进行统计
        for (int i = 0; i < bigData.length; i++) {
            topN.countTopN(bigData[i]);
        }
        
        //统计完毕,获取Top20的最大的20个数字
        long[] result = topN.getTopN();

        //打印统计结果(最大的20个数字的数组)
        System.out.println("最大的20个数字:" + Arrays.toString(result));
    }
    
}

运行TopN工具类的main方法,控制台输出从10000个百万级数字中获得的最大的20个数字,测试通过(为了方便查看,控制台输出内容适当做了换行操作):

最大的20个数字:
[9974849, 9977210, 9978159, 9978458, 9978577, 
9978882, 9982264, 9983253, 9983472, 9983855, 
9986296, 9987833, 9988433, 9989011, 9989025, 
9989105, 9989854, 9993490, 9997926, 9999579]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章