思路一
Hashmap + Minheap
维护一个最小堆,堆顶元素的频率是整个堆中最的。
- 如果堆未满的话,直接往堆中添加元素
- 如果对已满,那就把要添加的元素的频率与堆中频率最小的元素进行比较,如果要比较的元素频率大于堆顶,则添加,否则不添加
- 按照条件二,遍历map中所有的Entry后
- 遍历完整个频率map后,最小堆中则维护着频率最大的k个entry
- 遍历优先级队列,然后取出优先级队列中Entry 中的getKey()即可
hashmap统计频率,minheap从中选择k个最大的元素元素,
代码(java)
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
//统计频率的写法,
for(int n:nums){
map.put(n,map.getOrDefault(n,0) + 1);
}
Queue<Map.Entry<Integer,Integer>> minheap = new PriorityQueue<>(k,(a,b)->(a.getValue()-b.getValue()));
//k为堆的容量大小,
for(Map.Entry<Integer,Integer> entry: map.entrySet()){
//逐渐构建堆的过程,如果堆中的元素比较少于k个,直接添加
if(minheap.size()<k)
minheap.add(entry);
else{
Map.Entry<Integer,Integer> min = minheap.peek();
if(entry.getValue()>min.getValue())
{
minheap.poll();
minheap.add(entry);
}
}
}
List<Integer> res =new ArrayList<>();
while(!minheap.isEmpty()){
Map.Entry<Integer,Integer> entry = minheap.poll();
res.add(entry.getKey());
}
return res;
}
}
最大堆 HashMap + 最大堆
改进上述代码, 直接改成最大堆即可,遍历一边HashMap中堆元素,最后即可为何最大的top k的数,
使用 LinkedList 保存结果,然后链表尾部插入或者去链表头部插入,就可以采用加入该模块的情况
// use maxHeap. Put entry into maxHeap so we can always poll a number with largest frequency
public class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for(int n: nums){
map.put(n, map.getOrDefault(n,0)+1);
}
PriorityQueue<Map.Entry<Integer, Integer>> maxHeap =
new PriorityQueue<>((a,b)->(b.getValue()-a.getValue()));
for(Map.Entry<Integer,Integer> entry: map.entrySet()){
maxHeap.add(entry);
}
List<Integer> res = new ArrayList<>();
while(res.size()<k){
Map.Entry<Integer, Integer> entry = maxHeap.poll();
res.add(entry.getKey());
}
return res;
}
}
c++
typedef pair<int, int> P;
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> cnt;
for (int x : nums) cnt[x] ++;
priority_queue<P, vector<P>, greater<P> > q;
for (auto &x : cnt) {
if (q.size() < k)
q.push(make_pair(x.second, x.first));
else {
if (q.top().first < x.second) {
q.pop();
q.push(make_pair(x.second, x.first));
}
}
}
vector<int> ans;
while (!q.empty()) {
ans.push_back(q.top().second);
q.pop();
}
return ans;
}
};
时间复杂度
O(nlogk) ,空间复杂度为O(n)
降低时间复杂度的类型情况
Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.
** 题目条件**
利用桶排序算法的情况
如何构造桶
List [] bucket =new List[大小], 每一个都要创建开始
bucket[i] = new ArrayList<>()
HashMap + Bucket
HashMap 解决频率问题,然后每一个桶保持频率相同元素,然后添加即可,然后从最高频率开始填
这样我们从桶的后面向前面遍历,最先得到的就是出现次数最多的数字,我们找到k个后返回即可,参见代码如下
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
List<Integer> res = new ArrayList<>();
if (nums == null || nums.length < k) return res;
Map<Integer, Integer> map = new HashMap<>();
for (int num: nums) {
map.put(num, map.getOrDefault(num, 0)+1);
}
List[] buckets = new ArrayList[nums.length+1];
for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
int value = entry.getValue();
if (buckets[value] == null) buckets[value] = new ArrayList<>();
buckets[value].add(entry.getKey());
}
for (int i = buckets.length-1; i >= 0 && res.size() < k; i--) {
if (buckets[i] != null) res.addAll(buckets[i]);
}
return res;
}
}