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