-
完全二叉树定义,
-
满足 堆序型的完全二叉树 形成 :堆;
-
使用 优先级队列容器适配器 实现堆 ;
-
优先级队列:优先级队列中元素的出队顺序 与元素的优先级有关。
-
优先级队列中, 三个参数的意义:
template, typename Compare=std::less > class priority_queue{ //...... } 1. typename T:指定存储元素的具体类型; 2. typename Container:指定 priority_queue 底层使用的基础容器, 3. typename Compare:指定容器中评定元素优先级所遵循的排序规则, 默认使用std::less 按照元素值从大到小进行排序, 还可以使用std::greater 按照元素值从小到大排序, 但更多情况下是使用自定义的排序规则。
上述的 数据结构, 在 C++ STL 标准模版库中, 使用了 priority_queue: 优先级队列容器适配器实现了;
-
pair
: 关联式容器 中的 pair 类模板: 用来将2个普通元素 first, second 创建成一个新的元素,称为键值对 ; -
unordered_map: 关联式容器, 其中存储的元素, 都是一个一个的 “键值对” (
);
并且 unordered_map 底层实现是一个哈希表,key 无序,且不可重复, key 不可以修改; -
容器中元素的遍历, 这里是 unordered_map 容器, 采用 增强型的 for 循环方式;
-
auto: 自动类型推导;
-
decltype: 声明类型, declare type;
-
向量容器中的 emplace_back() 方法: 在序列尾部生成一个元素;
-
priority_queue 优先级队列 容器适配器: 中的 emplace() 方法: 此方法的作用, 根据既定的排序规则,在容器适配器适当的位置直接 生成该 新元素;
(emplace(Args&&… args),而对于 类对象来说, 可能需要多个数据构造出一个对象, 所以使用 Agrs … args 表示构造一个存储类型的元素,所需要的数据)
构建一个比较函数, 返回bool 类型; 用于比较两对
构建 题目任务的 topK 函数, 返回一个向量容器, 其中存储的元素 是前 K 个 高频出现的 元素值;
-
新建一个 无序的关联式容器 unordered_map<> uMap, 用于存放,数组元素以及 对应元素出现的次数;
-
遍历给定的数组, 将给定数组中的元素作为key 存储在 uMap 中, 该元素出现的次数作为 value 存储在 uMap 中;
-
使用优先级队列,构建一个小根堆; 注意priority_que 传入的 三个参数的意义, 使用该优先级队列 按照第三个传入的参数 规则, 形成一个小顶堆;
-
遍历 uMap 中
, 将其中元素存入到小根堆中,并作维护大小为 K 的 小顶堆;
4.1 如果堆的大小 == K 个 键值对:
并且此时如果, 小根堆的堆顶中的键值对,, value 小于当前 uMap 中的 freq, 则将顶层的 移除,将当前的uMap 中的 加入到队列中;
4.2 否则, 堆的大小小于 K 时, 则直接将当前uMap 中的加入堆中; -
创建一个结果集 向量容器 ret; 用于存储小根堆中 每一对
中的 key; -
遍历小根堆, 当小根堆不为空时, 将小根堆中每一对
中的 key , 存入到 ret 中, 并移除当前堆顶的 . -
返回 ret;
#include "unordered_map" // 用于保存数组的元素, 以及元素出现的次数; 其中包含了 pair的模版 #include "vector" // 用于保存最终top k 个元素; #include "queue" // 调用 priority_queue 构建 小根堆; using namespace std; class Solution{ public: // 制定优先级队列中, 优先级的规则定义; 即判断两个元素 对应的频率更大; static bool cmp_fun(pair & m, pair & n ) { return m.second > n.second; // 按照 value 的大小作为 优先级 标注; } vector topKFreq(vector & nums, int k){ // 1. 新建一个 unordered_map 用于存放元素和次数; unordered_map uMap; // 2. 遍历数组, 将其中的元素, 以及对应次数存入到 uMap 中; for(auto ele: nums) uMap[ele]++; // : key 代表 数组中的元素, value: 代表该元素出现的次数; // 3. 使用优先级队列, 构造小根堆; 注意优先级队列三个参数的意义; priority_queue , vector >, decltype(&cmp_fun) > heap(cmp_fun); // 4. 遍历uMap 将其中的 , 形成一个 大小为K 的小根堆;; for(auto& [ele, freq]: uMap){ if(heap.size() == k){ // 如果小根堆的大小为 K, 则此时开始 维护 小根堆; if(heap.top().second < freq){ // 如果堆顶的键值对中的 freq 小于当前的 freq, 则移除堆顶的 键值对, 存入当前的键值对; heap.pop(); heap.emplace(ele, freq); // 使用 emplace() 优先级队列中的方法; } }else{ heap.emplace( ele, freq);} // 否则,堆的大小 小于 K, 直接存入键值对; } // 5. 创建一个结果集, 用于保存小根堆中 的 key 数值; vector result; // 6. 当小根堆不为空时, 将其中每一对的 key 存入到结果集中; while (!heap.empty()){ result.push_back(heap.top().first); heap.pop(); } return result; } };
// 时间复杂度:O(nlogk)
// 空间复杂度:O(n)
class Solution {
public:
// 小顶堆
class mycomparison {
public:
bool operator()(const pair& lhs, const pair& rhs) {
return lhs.second > rhs.second;
}
};
vector topKFrequent(vector& nums, int k) {
// 要统计元素出现频率
unordered_map map; // map
for (int i = 0; i < nums.size(); i++) {
map[nums[i]]++;
}
// 对频率排序
// 定义一个小顶堆,大小为k
priority_queue, vector>, mycomparison> pri_que;
// 用固定大小为k的小顶堆,扫面所有频率的数值
for (unordered_map::iterator it = map.begin(); it != map.end(); it++) {
pri_que.push(*it);
if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
pri_que.pop();
}
}
// 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
vector result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = pri_que.top().first;
pri_que.pop();
}
return result;
}
};



