LeetCode 347.前K个高频元素

132 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目:给定一个整数数组和一个整数k,要求返回在数组中出现频率前k高的元素。要求算法的时间复杂度必须优于O(nlogn)O(nlogn)

解题思路

本题可拆解为:

  1. 统计各个元素的频率。
  2. 输出前k个频率较高的元素。

统计元素的频率很简单,我们可以使用HashMap来进行统计,难点在于统计完之后如何输出前K个频率较高的元素。

这里我的思路是使用一个集合将这些元素装入,因为Java中集合有List.sort()方法,我们可以调用该方法定义排序规则即可,最后输出前k个元素到一个数组中即可得到最终答案,代码如下:

public int[] topKFrequent(int[] nums, int k) {
    HashMap<Integer, Integer> map = new HashMap<>();
    for(int i=0;i<nums.length;i++){
        if(!map.containsKey(nums[i])){
            map.put(nums[i], 0);
        }
        map.put(nums[i], map.get(nums[i])+1);
    }
    List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
    list.sort((o1, o2) -> o2.getValue() - o1.getValue());

    int[] result = new int[k];
    for(int i=0;i<k;i++){
        result[i] = list.get(i).getKey();
    }
    return result;
}

时间复杂度为O(nlogn)O(nlogn),主要在于集合排序的时间复杂度,空间复杂度为O(1)O(1)

上述方法是较为简单的,看了题解之后发现有人使用优先级队列来进行前K个元素的获取,在使用优先级队列的时候需要将map进行拆解到一个数组中,之后使用这个数组重写比较器方法,后面的操作都类似,个人感觉没有上面的好理解,代码如下:

 public int[] topKFrequent2(int[] nums, int k) {
     Map<Integer, Integer> map = new HashMap<>();
     for (int num : nums) {
         map.put(num, map.getOrDefault(num, 0) + 1);
     }

     // int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
     PriorityQueue<int[]> queue = new PriorityQueue<int[]>((m, n) -> m[1] - n[1]);
     for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
         int num = entry.getKey(), count = entry.getValue();
         if (queue.size() == k) {
             if (queue.peek()[1] < count) {
                 queue.poll();
                 queue.offer(new int[]{num, count});
             }
         } else {
             queue.offer(new int[]{num, count});
         }
     }
     int[] ret = new int[k];
     for (int i = 0; i < k; ++i) {
         ret[i] = queue.poll()[0];
     }
     return ret;
 }

时间复杂度和空间复杂度和上面一样。