347.前k个高频元素
思路:最小堆
1.遍历数组,用哈希表记录每个数字出现的频率
2.对哈希表中的频率使用最小堆找出前k大的频率
具体思路同215题
1.adjMinHeap
- 维护最小堆
left
和right
分别计算当前root
结点的左右子结点索引。- 选出
root
、left
、right
之间的最小值,更新minimum
。 - 如果
minimum
不是root
,交换nums[root]
和nums[minimum]
,并递归调整子树。
2.buildMinHeap
- 构建最小堆
从 最后一个非叶子节点 开始,向上调整堆。
k / 2 - 1
是最后一个非叶子节点的索引,从它开始向上调整堆的结构。
3.findKthLargest
- 找到第 k
大的元素
(1)构建大小为 k 的最小堆
(2)遍历 nums[k:]
:
nums[i] > nums[0]
- 说明
nums[i]
可能是k
个最大元素之一。 - 用
nums[i]
替换堆顶nums[0]
,并调整堆,使nums[0]
仍然是k
个元素中的最小值。
- 说明
nums[i] < nums[0]
nums[i]
不可能是前k
大元素之一,忽略。
最终 nums[0]
为第 k
大的元素:
- 最小堆中的
k
个元素 是 前k
大的数,堆顶nums[0]
是它们中最小的,即第k
大的数。
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int>hash;
vector<int>frequent;
//遍历数组记录频率
for(int i=0;i<nums.size();i++){
hash[nums[i]]++;
}
//遍历哈希表记录频率数组
for(auto &fre:hash){
frequent.push_back(fre.second);
}
//将前k个频率放入堆中
buildMinHeap(frequent,k);
//从第k+1个开始遍历,与堆顶比较
for(int i=k;i<frequent.size();i++){
if(frequent[i]<frequent[0]) continue;
swap(frequent[i],frequent[0]);
adjMinHeap(frequent,0,k);
}
frequent.assign(frequent.begin(),frequent.begin()+k);
vector<int>result;
for(auto &pair:hash){
if (find(frequent.begin(), frequent.begin()+k, pair.second) != frequent.begin()+k) {
result.push_back(pair.first);
}
}
return result;
}
void adjMinHeap(vector<int>& nums,int root,int k){
int left=2*root+1,right=2*root+2,mininum=root;
//选出三个结点最小的
if(left<k && nums[left]<nums[mininum])
mininum=left;
if(right<k && nums[right]<nums[mininum])
mininum=right;
if(nums[mininum]!=nums[root]){
swap(nums[root],nums[mininum]);
adjMinHeap(nums,mininum,k);//重新调整堆
}
}
void buildMinHeap(vector<int>&nums,int k){
//从最后一个非叶子结点开始 k/2-1
for(int i=k/2-1;i>=0;i--){
adjMinHeap(nums,i,k);
}
}
};