TopK问题

作者: icecrea | 来源:发表于2017-11-27 19:21 被阅读47次

    从文件中输出请求最频繁的10个 HTTP 接口,及其相应的请求数量
    数据格式如下

    GET /mdc/city/queryAllCities.json?arg1=var1&arg2=var2
    GET /twell/public.htm?arg1=var1&arg2=var2
    GET /duty/getToDoDutyCount.json?arg1=var1&arg2=var2
    GET /notification/queryMessageByUid.htm?arg1=var1&arg2=var2
    GET /twell/querytwellDetailForMobile.htm?arg1=var1&arg2=var2
    GET /twell/public.htm?arg1=var1&arg2=var2
    GET /twell/private.htm?arg1=var1&arg2=var2
    POST /tag/getSearchTagList.json
    POST /user/postDeviceInfo.htm
    

    从大数据中找到TopK个数,比较经典的就是维护一个小根堆,堆顶是堆中最小的元素,每次通过移除堆顶,重新堆排序来维护这个结构。
    在java中有一种数据结构可以维护一个堆,为PriorityQueue,具体实现暂不深究。
    每次判断队列顶端元素和新元素大小决定是否入队,当超过维护的上限时移除堆顶元素。

      public void findKthLargest(int[] nums, int k) {
            PriorityQueue<Integer> minQueue = new PriorityQueue<>(k);
            for (int num : nums) {
                if (minQueue.size() < k || num > minQueue.peek())
                    minQueue.offer(num);
                if (minQueue.size() > k)
                    minQueue.poll();
            }
            for(int i=0;i<k;i++){
                System.out.println(minQueue.poll());
            }
        }
    

    对于这道题,一开始想法是用java的hashmap解决,然后用Collections.sort()全部排序取前十

        public static void requestTopK(int k) {
            final HashMap<String, Integer> hashMap = new HashMap();
            try {
                IOUtils.readLines(file_path, new LineProcessor<String>() {
                    @Override
                    public boolean processLine(String s) throws IOException {
                        String key = s.split(" ")[1];
                        if (hashMap.containsKey(key)) {
                            hashMap.put(key, Integer.valueOf(hashMap.get(key)) + 1);
                        } else {
                            hashMap.put(key, 1);
                        }
                        return true;
                    }
    
                    @Override
                    public String getResult() {
                        return null;
                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
            List<Map.Entry<String, Integer>> list = new ArrayList<>();
            list.addAll(hashMap.entrySet());
            Ordering ordering = Ordering.natural().reverse().onResultOf(new Function<Map.Entry<String, Integer>, Comparable>() {
    
                @Nullable
                @Override
                public Comparable apply(@Nullable Map.Entry<String, Integer> stringIntegerEntry) {
                    return stringIntegerEntry.getValue();
                }
            });
            Collections.sort(list, ordering);
            for (int i = 0; i < k; i++) {
                System.out.println(list.get(i));
            }
    
        }
    

    但是其实这道题可以结合guava api来做,使用multset数据结构

      public static void requestTopK2(int k) {
            final Multiset multiset = HashMultiset.create();
            try {
                IOUtils.readLines(file_path, new LineProcessor<String>() {
    
                    @Override
                    public boolean processLine(String s) throws IOException {
                        String urlContent = s.split(" ")[1];
                        multiset.add(urlContent);
                        return true;
                    }
    
                    @Override
                    public String getResult() {
                        return null;
                    }
                });
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
            findKthLargest(multiset.elementSet(), k, multiset);
        }
    

    PriorityQueue存储String,自定义实现接口Comparator(),multset.count()时间复杂度0(1),根据String对应的出现次数定义顺序。
    另外注意的是,原输出方式是每次移除堆顶输出,但堆顶是堆中最小元素,相当于从小到大输出这十个数。如果从大到小输出可以利用栈结构。

        public static void findKthLargest(Set<String> set, int k, final Multiset multiset) {
            PriorityQueue<String> minQueue = new PriorityQueue<>(k, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return multiset.count(o1) - multiset.count(o2);
                }
            });
            for (String s : set) {
                if (minQueue.size() < k || multiset.count(s) > multiset.count(minQueue.peek())) {
                    minQueue.offer(s);
                }
                if (minQueue.size() > k) {
                    minQueue.poll();
                }
            }
            Stack<String> stack = new Stack();
            for (int i = 0; i < k; i++) {
                String poll = minQueue.poll();
                stack.push(poll);
            }
            for (int i = 0; i < k; i++) {
                String url = stack.pop();
                System.out.println(url + " " + multiset.count(url));
            }
        }
    

    通过multset这种结构重新设计findKthLargest()方法时更简单了一点。如果是List<Map.Entry<String, Integer>>形式的话,实现的代码非常长也非常难看。

    相关文章

      网友评论

        本文标题:TopK问题

        本文链接:https://www.haomeiwen.com/subject/ibidbxtx.html