Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 6366|回复: 0

Java解决TopK问题(使用集合和直接实现)

[复制链接]
  • TA的每日心情
    奋斗
    3 天前
  • 签到天数: 790 天

    [LV.10]以坛为家III

    2049

    主题

    2107

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    722766
    发表于 2021-4-18 12:54:28 | 显示全部楼层 |阅读模式

    在处理大量数据的时候,有时候往往需要找出Top前几的数据,这时候如果直接对数据进行排序,在处理海量数据的时候往往就是不可行的了,而且在排序最好的时间复杂度为nlogn,当n远大于需要获取到的数据的时候,时间复杂度就显得过高。
    使用最小堆或者最大堆可以很好地解决Top大问题或者Top小问题。

    • Top大问题解决思路:使用一个固定大小的最小堆,当堆满后,每次添加数据的时候与堆顶元素比较,若小于堆顶元素,则舍弃,若大于堆顶元素,则删除堆顶元素,添加新增元素,对堆进行重新排序。
    • Top小问题解决思路:使用一个固定大小的最大堆,当堆满后,每次添加数据到时候与堆顶元素进行比较,若大于堆顶元素,则舍弃,若小于堆顶元素,则删除堆顶元素,添加新增元素,对堆进行重新排序。

    对于n个数,取Top m个数,时间复杂度为O(nlogm),这样在n较大情况下,是优于nlogn的时间复杂度的。

    比如10000个数据,取前100大的数,那么时间复杂度就是O(10000log100)。
    因为在插入数据的时候需要遍历元素时间复杂度达到了O(10000),然后每次插入过程中进行调整的复杂度为O(log100),所以总体时间复杂度为O(10000log100)。

    使用Java类库集合实现

    Java集合中的PriorityQueue就可以实现最大堆或者最小堆,从名字可以知道该集合是优先队列,数据结构中的优先队列就是使用堆来实现的。

    // 底层通过一个Object类型数据保存元素
    transient Object[] queue;
    
    // 通过Comparator制定比较方法
    private final Comparator<? super E> comparator;
    
    
    // 其中一个构造函数
    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
    

    下面就使用PriorityQueue来实现最小堆和最大堆。

    • 在构造PriorityQueue的时候需要传入一个size和一个比较函数,制定堆中元素比较规则。
    • 重写compare(o1, o2)方法,最小堆使用o1 - o2,最大堆使用o2 - o1。
    public class TopK<E extends Comparable> {
        private PriorityQueue<E> queue;
        private int maxSize; //堆的最大容量
    
        public TopK(int maxSize) {
            if (maxSize <= 0) {
                throw new IllegalStateException();
            }
            this.maxSize = maxSize;
            this.queue = new PriorityQueue<>(maxSize, new Comparator<E>() {
                @Override
                public int compare(E o1, E o2) {
                    // 最大堆用o2 - o1,最小堆用o1 - o2
                    return (o1.compareTo(o2));
                }
            });
        }
    
        public void add(E e) {
            if (queue.size() < maxSize) {
                queue.add(e);
            } else {
                E peek = queue.peek();
                if (e.compareTo(peek) > 0) {
                    queue.poll();
                    queue.add(e);
                }
            }
        }
    
        public List<E> sortedList() {
            List<E> list = new ArrayList<>(queue);
            Collections.sort(list);
            return list;
        }
    
        public static void main(String[] args) {
            int[] array = {4, 5, 1, 6, 2, 7, 3, 8};
            TopK pq = new TopK(4);
            for (int n : array) {
                pq.add(n);
            }
            System.out.println(pq.sortedList());
        }
    }
    

    运行结果:

    使用Java实现

    通过上述讲述,基本了解最大堆和最小堆情况以及它们与TopK问题的关系,上面是使用集合实现,下面使用Java来实现最小堆,并解决TopK大问题。

    • 限定数据大小。
    • 若堆满,则插入过程中与堆顶元素比较,并做相应操作。
    • 每次删除堆顶元素后堆做一次调整,保证最小堆特性。
    public class TopK {
        int[] items;
        int currentSize = 0;
    
    	// 初始化为size + 1,从下标1开始保存元素。
        public TopK(int size) {
            items = new int[size + 1];
        }
    
    	// 插入元素
        public void insert(int x) {
            if (currentSize == items.length - 1) {
                if (compare(x, items[1]) < 0) {
                    return;
                } else if (compare(x, items[1]) > 0) {
                    deleteMin();
                }
            }
    
            int hole = ++currentSize;
            for (items[0] = x; compare(x, items[hole / 2]) < 0; hole /= 2) {
                items[hole] = items[hole / 2];
            }
            items[hole] = x;
        }
    
    	// 删除最小堆中最小元素
        public int deleteMin() {
            int min = items[1];
            items[1] = items[currentSize--];
            percolateDown(1);
            return min;
        }
    
    	// 下滤
        public void percolateDown(int hole) {
            int child;
            int temp = items[1];
    
            for (; hole * 2 <= currentSize; hole = child) {
                child = 2 * hole;
                if (child != currentSize && compare(items[child + 1], items[child]) == -1) {
                    child++;
                }
                if (compare(items[child], temp) < 0) {
                    items[hole] = items[child];
                } else {
                    break;
                }
            }
            items[hole] = temp;
        }
    
    	// 制定比较规则
        public static int compare(int a, int b) {
            if (a < b) {
                return -1;
            } else if (a > b) {
                return 1;
            }
            return 0;
        }
    
        public static void main(String[] args) {
            TopK topK = new TopK(10);
            for (int i = 1; i <= 100; i++) {
                topK.insert(i);
            }
            for (int j = 1; j <= topK.currentSize; j++) {
                System.out.print(topK.items[j] + " ");
            }
            System.out.println();
        }
    }
    

    运行结果:

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-9-13 14:36 , Processed in 0.063318 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表