0
点赞
收藏
分享

微信扫一扫

java-Top K问题「海量数据中筛选出最大或最小的10个数」

吴wuwu 2022-01-20 阅读 25

java 优先级队列 PriorityQueue可实现自动排序功能

数据结构:小顶堆

源码和原理: https://baijiahao.baidu.com/s?id=1665383380422326763&wfr=spider&for=pc

1. 海量数据中筛选出最大或最小的10个数

package com.eaglefly.common.service;

import org.junit.Test;
import java.io.*;
import java.util.PriorityQueue;
import java.util.Random;

/**
 * 计算海量数据中的top K
 *
 * 在topkdata.txt 文件中模拟海量数据
 * 使用堆排序筛选top K个数据
 */
public class TopKDemo {

    private final Random random = new Random();

    /**
     * 存储1000万条数据的文件
     */
    private final File file = new File("topkdata.txt");

    /**
     * java中有「优先级队列」来做为「小顶堆」来使用
     */
    private final PriorityQueue priorityQueue = new PriorityQueue<>(10);

    @Test
    public void computeTopK() {

        FileReader fileReader = null;
        BufferedReader bufferedReader = null;

        try {
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                //todo 核心方法
                addToTopKQueue(Long.valueOf(line));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // close()
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            //todo 输出Long 类型的数据的最大值,便于判断top 10 的数据是否正确
            System.out.println("Long.MAX_VALUE = " + Long.MAX_VALUE);

            //todo 输出 PriorityQueue队列中的数据
            Long target;
            while ((target = (Long) priorityQueue.poll()) != null) {
                System.out.println("target = " + target);
            }
        }
    }

    /**
     * init方法仅运行一次即可,是为准备模拟数据
     */
    @Test
    public void init() {
        long start = System.currentTimeMillis();
        System.out.println("init");
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(file, true);

            //todo 先用1000万数据,多了电脑可能受不了
            for (int i = 0; i < 10000000; i++) {
                fileWriter.write(String.valueOf(random.nextLong()) + System.lineSeparator());
            }

            //todo 写入10个接近long的最大值的数,便于取出是验证正确结果
            for (int i = 0; i < 10; i++) {
                fileWriter.write(String.valueOf(Long.MAX_VALUE - i) + System.lineSeparator());
            }

            //flush()
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("用时:" + (System.currentTimeMillis() - start));
        }
    }

    /**
     * 判断遍历到的数字是否比小顶堆中堆顶的数字要大:
     * 1)如果小的话继续向后遍历;
     * 2)如果大的话则将堆顶的数字弹出队列,并将该数字添加到堆当中,并重新调整小顶堆。
     *
     * @param target 遍历海量数据中的某个数字
     */
    public void addToTopKQueue(Long target) {
        if (priorityQueue.size() < 10) {
            priorityQueue.add(target);
        } else {
            // 找到堆顶元素的值
            Long head = (Long) priorityQueue.peek();
            if (target > head) {
                priorityQueue.poll();
                priorityQueue.add(target);
            }
        }
    }
}

2. 海量数据中筛选出第K最大或最小的数据

public int findKthLargest(int[] nums, int k){
    Queue<Integer> priorityQueue = new PriorityQueue<>();
    for(int num : nums){
        queue.add(num);
        // 当queue的大小超过K的时候,则弹出堆顶的最小元素
        if(queue.size() > k){
            queue.poll();
        }
    }
    // 输出大小为K的队列中队首的元素值(也就是堆顶的元素)
    return queue.peek();
}
举报

相关推荐

0 条评论