题目相关
问题描述
给你一个整数数组 nums 和一个整数 k,请你用一个字符串返回其中出现频率前 k 高的元素。请按升序排列。 你所设计算法的时间复杂度必须优于 O(nlogn),其中n是数组大小。
输入描述
- nums: 一个正整数数组
- k: 一个整数
返回
返回一个包含 k 个元素的字符串,数字元素之间用逗号分隔。数字元素按升序排列,表示出现频率最高的 k 个元素。
参数限制
- 1 <= nums[i] <= 10^4
- 1 <= nums.length <= 10^5
- k 的取值范围是 [1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
测试样例
样例1:
输入:nums = [1, 1, 1, 2, 2, 3], k = 2
输出:"1,2"
解释:元素 1 出现了 3 次,元素 2 出现了 2 次,元素 3 出现了 1 次。因此前两个高频元素是 1 和 2
样例2:
输入:nums = [1], k = 1
输出:"1"
解题
数据结构
对于统计前k个高频元素的问题,借卡哥的思路就是使用两种数据结构:
- 哈希表
- 用于统计每个元素的出现频率。哈希表的插入和查找操作的时间复杂度为
O(1),非常适合用于频率统计
- 用于统计每个元素的出现频率。哈希表的插入和查找操作的时间复杂度为
- 优先队列(底层就是堆实现的)
- 用于维护前
k个高频元素。优先队列可以在O(nlog k)的时间内插入和删除元素,非常适合用于动态维护前k个高频元素。而我们此处要使用的是小顶堆,因为每次移动更新堆的时候,每次弹出的元素是最大元素,就无法保留前k个高频元素,所以我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。 - 引用卡哥的图(作者代码随想录)
- 用于维护前
算法思路
- 遍历数据nums,通过哈希表unorder_map将每个元素的出现频率作统计。
- 维护前k个高频的元素,在优先队列有元素插入时,如果队列的大小超过k,则会弹出队列中最小的元素,也就是频率最低的元素
- 构建结果数组
result,从优先队列中弹出元素,依次将频率从高到低出现的元素依次填入结果数组result - 排序结果数组,对数组
result进行排序,确保元素按照升序排列 - 转化为字符串,通过stringstream将
result转换为字符串,元素之间用逗号分隔
核心代码
- 统计频率并通过优先队列保存维护
unordered_map<int, int> map;
for (int i = 0; i < nums.size(); i++){
map[nums[i]]++;
}
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparision> q;
for (unordered_map<int,int>::iterator it = map.begin(); it != map.end(); it++){
q.push(*it);
if (q.size() > k){
q.pop();
}
}
2. 构建小顶堆,通过对比pair中的第二个元素,也就是出现的频率进行排序
class mycomparision{
public:
bool operator()(const pair<int, int>& lhs, pair<int, int>& rhs){
return lhs.second > rhs.second;
}
};
3. 将数组转换为字符串,通过使用stringstream将数组之前插入“,”分割,输出符合题目的格式
std::string vectorToString(const vector<int>& vec){
stringstream ss;
for (size_t i = 0; i < vec.size(); i++){
if (i != 0){
ss << ",";
}
ss << vec[i];
}
return ss.str();
}
全部代码
class mycomparision{
public:
bool operator()(const pair<int, int>& lhs, pair<int, int>& rhs){
return lhs.second > rhs.second;
}
};
std::string vectorToString(const vector<int>& vec){
stringstream ss;
for (size_t i = 0; i < vec.size(); i++){
if (i != 0){
ss << ",";
}
ss << vec[i];
}
return ss.str();
}
std::string solution(std::vector<int> nums, int k) {
// Please write your code here
unordered_map<int, int> map;
for (int i = 0; i < nums.size(); i++){
map[nums[i]]++;
}
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparision> q;
for (unordered_map<int,int>::iterator it = map.begin(); it != map.end(); it++){
q.push(*it);
if (q.size() > k){
q.pop();
}
}
vector<int> result(q.size());
for (int i = k - 1; i >= 0; i--){
result[i] = q.top().first;
q.pop();
}
sort(result.begin(), result.end());
return vectorToString(result);
}
总结
通过使用哈希表和优先队列,我们可以在 O(n log k) 的时间复杂度、O(n)的空间复杂度内解决这个问题。哈希表用于快速统计元素的频率,优先队列用于动态维护前 k 个高频元素。最后,我们将结果数组排序并转换为字符串。