[LeetCode] 347. Top K Frequent Elements

Given a non-empty array of integers, return the k most frequent elements.

For example,
Given [1,1,1,2,2,3] and k = 2, return [1,2].

Note: 

  • You may assume k is always valid, 1 ≤ k ≤ number of unique elements.

  • Your algorithm's time complexity must be better than O(n log n), where n is the array's size.

题目:给定一个元素都是整数的非空数组、一个目标整数k,返回数组中出现频率排在前k个(从大到小)的元素。假设k总是满足1<=k<=数组中不重复的元素个数。要求算法的时间复杂度优于O(nlogn),n是数组长度。

实现思路

  • 用HashMap统计每个元素的出现频率(次数),key = 元素,value = 该元素出现的次数。
  • 得到HashMap需要遍历数组,这个过程的时间复杂度是O(n)。如果再按照出现频率对键值对进行排序,整个算法的时间复杂度就不会优于O(nlogn),因为基于比较的排序算法的最优时间复杂度是O(nlogn)。
  • 假设数组长度是n,则每一个元素的出现频率最少是1次、最多是n次。因此,可以创建这样一个数组A,使每个index表示所有可能出现的频率(从1到n,index=0没有实际意义),每个index是原数组中出现频率等于index的元素集合。如果令A[i]=j,则j是原数组中所有出现频率为i的元素构成的集合。
  • 需要注意的是,A[i]可能是空集,因为原数组中可能不存在出现频率为i的元素。
  • 由于index本身是升序排列且index表示出现频率,那么数组A的最后k个非null元素(非空集合)的补集就是题目的结果。
  • 上述做法的好处是,只需遍历一次HashMap,将每个key放进A[value]的集合中即可,不用再对HashMap的所有键值对单独进行一次排序。因此,整个算法的时间复杂度是O(n)。
class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {
        if (nums == null) throw new IllegalArgumentException("argument is null");
        int length = nums.length;
        Map<Integer, Integer> map = new HashMap<>();
        List<Integer>[] array = new List[length + 1];
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            if (map.containsKey(nums[i]))
                map.put(nums[i], map.get(nums[i]) + 1);
            else
                map.put(nums[i], 1);
        }
        for (Integer key : map.keySet()) {
            int times = map.get(key);
            if (array[times] == null)
                array[times] = new ArrayList<>();
            array[times].add(key);
        }
        for (int i = length; i >= 1 && result.size() < k; i--) {
            if (array[i] != null)
                result.addAll(array[i]);
        }
        return result;
    }
}

参考资料:

https://leetcode.com/problems/top-k-frequent-elements/discuss/81602/Java-O(n)-Solution-Bucket-Sort

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值