leetcode347 —— n中topK && PriorityQueue(Heap) && Map遍历

题目要求:求前K个最频繁出现的数字。

1.很容易想到,使用HashMap<Integer,Integer>来存储<number,frequency>键值对

1         int n = nums.length;
2         Map<Integer, Integer> map = new HashMap<>(n);
3 
4         for (int num : nums) {
5             map.put(num, map.getOrDefault(num, 0) + 1);
6         }    

2.接下来非常自然的思路就是:

  • 对<number,frequency>按照frequency进行降序排序
  • 返回前k个<number,frequency>的number

 

  根据题目的黑字提示:“你可以按 任意顺序 返回答案。”。可以计算出第K频繁的数的frequency,接着遍历map,返回所有frequency >= Kth的number即可:

 1         Integer[] temp = map.values().toArray(new Integer[map.size()]);
 2         Arrays.sort(temp);
 3         int[] ans = new int[k];
 4         int KthCount = temp[temp.length - k], i = 0;
 5 
 6         for (Map.Entry<Integer, Integer> kv : map.entrySet()) {
 7             if (kv.getValue() >= KthCount) {
 8                 ans[i++] = kv.getKey();
 9             }
10         }

 

因为时间超过5%,想到了对map遍历时是否可以直接遍历map中的键值对,于是:

1         for (Map.Entry<Integer, Integer> kv : map.entrySet()) {
2             if (kv.getValue() >= KthCount) {
3                 ans[i++] = kv.getKey();
4             }
5         }

 

进阶的时间要求想不出来,于是看了题解,有了以下收获:

题目最终需要返回的是前 kk 个频率最大的元素,可以想到借助堆这种数据结构,对于 kk 频率之后的元素不用再去处理,进一步优化时间复杂度。

 

  • 求前 k 大,用小根堆,求前 k 小,用大根堆
    •         PriorityQueue<int[]> minHeap = new PriorityQueue<>(k,
                      new Comparator<int[]>() {
                          @Override
                          public int compare(int[] o1, int[] o2) {
                              //升序
                              return o1[1] - o2[1];
                              //降序
                              return o2[1] - o1[1];
                          }
                      });

       


  • topk (前k大)用小根堆,维护堆大小不超过 k 即可。每次压入堆前和堆顶元素比较,如果比堆顶元素还小,直接扔掉,否则压入堆。检查堆大小是否超过 k,如果超过,弹出堆顶。复杂度是 nlogk
  • 避免使用大根堆,因为你得把所有元素压入堆,复杂度是 nlogn,而且还浪费内存。如果是海量元素,那就挂了。

 

        PriorityQueue<int[]> minHeap = new PriorityQueue<>(k,
                new Comparator<int[]>() {
                    @Override
                    public int compare(int[] o1, int[] o2) {
                        return o1[1] - o2[1];
                    }
                });
        
        int count = 0
        for (Map.Entry<Integer, Integer> kv : map.entrySet()) {
            //前K个键值对不需要判断
            if (count++ < k) {
                minHeap.add(new int[]{kv.getKey(), kv.getValue()});
            } else if (kv.getValue() > minHeap.peek()[1]) {
                minHeap.poll();
                minHeap.add(new int[]{kv.getKey(), kv.getValue()});
            }
        }

 

posted @ 2021-10-23 21:12  Mirror559  阅读(132)  评论(0编辑  收藏  举报