当前位置:   article > 正文

LeetCode:347. 前 K 个高频元素_输入一个整数数组和一个整数,请输出其中出现频率前高的元素,设计算法并写出伪代码

输入一个整数数组和一个整数,请输出其中出现频率前高的元素,设计算法并写出伪代码

347. 前 K 个高频元素

    

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

思路:

  1. 要统计元素出现次数(通过map:cnt)
  2. 对次数排序(通过优先级队列:pq)
  3. 找出前K个高频元素

时间复杂度:O(nlogk),n 表示数组的长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n);接着,遍历用于存储元素频率的 map,如果元素的频率大于大顶堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,这里维护堆的数目是 k,所以这一系列操作的时间复杂度是 O(nlogk) 的;因此,总的时间复杂度是 O(nlog⁡k)。

空间复杂度: O(n),最坏情况下(每个元素都不同),map 需要存储 n 个键值对,优先队列需要存储 k 个元素,因此,空间复杂度是 O(n)。

  1. vector<int> topKFrequent(vector<int>& nums, int k) {
  2. priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // 优先级队列(大顶堆)
  3. unordered_map<int, int> cnt; // 负责统计每个元素出现的次数 key:元素值 value:元素出现次数
  4. // for (auto num : nums) cnt[num]++;
  5. for (int i = 0; i < nums.size(); i++)
  6. {
  7. cnt[nums[i]] += 1;
  8. }
  9. // for (auto kv : cnt) {
  10. // pq.push({kv.second, kv.first});
  11. // while (pq.size() > k) pq.pop();
  12. // }
  13. for (unordered_map<int, int>::iterator it = cnt.begin(); it != cnt.end(); it++)
  14. {
  15. pq.push({it->second, it->first}); // 根据map中的key(次数)来排序,value为次数对应的元素值
  16. if (pq.size() > k) // pq始终维护长度为k的队列(此处提前限定了pq长度为k,下面while不用for (int i = 0; i < k; i++) 了)
  17. {
  18. pq.pop();
  19. }
  20. }
  21. // vector<int> res;
  22. // while (!pq.empty())
  23. // {
  24. // res.push_back(pq.top().second);
  25. // pq.pop();
  26. // }
  27. // return res;
  28. vector<int> res;
  29. while (!pq.empty())
  30. {
  31. res.push_back(pq.top().second); // 从队首依次从大到小获取每个元素的second域(元素值),并存入v
  32. pq.pop();
  33. }
  34. return res;
  35. }

这道题做的快自闭了,STL的优先级队列刚开始自己写语法都有问题 = = !哎难受~ 


补充一波Go实现的解法,不过可能时间复杂度不满足题目要求的优于 O(n log n) ,因为排序过程也有自己的时间复杂度,且算是一种方法吧 - - !

  1. func topKFrequent(nums []int, k int) []int {
  2. // 1.利用map统计每个元素出现频次
  3. m := make(map[int]int)
  4. for _, val := range nums {
  5. m[val] += 1
  6. }
  7. // 2.将map转为结构体数组,再利用sort包按结构体的Val"从大到小"排序
  8. type KV struct {
  9. Key int
  10. Val int
  11. }
  12. kvSlice := make([]KV, 0)
  13. for k, v := range m {
  14. kv := KV{k, v}
  15. kvSlice = append(kvSlice, kv)
  16. }
  17. sort.Slice(kvSlice, func(i, j int) bool { // 对结构体的val值"从大到小"排序
  18. return kvSlice[i].Val > kvSlice[j].Val
  19. })
  20. fmt.Println(kvSlice)
  21. // 3.输出前k个高频元素
  22. res := make([]int, 0)
  23. for i := 0; i < k; i++ {
  24. res = append(res, kvSlice[i].Key)
  25. }
  26. return res
  27. }
  28. // 上述过程简化版:
  29. func topKFrequent(nums []int, k int) []int {
  30. m := make(map[int]int, 0)
  31. for i := 0; i < len(nums); i++ {
  32. m[nums[i]] += 1
  33. }
  34. // 去重后的nums数组
  35. res := make([]int, 0)
  36. for k, _ := range m {
  37. res = append(res, k)
  38. }
  39. // 根据【出现频次】对【去重后的nums数组】排序
  40. sort.Slice(res, func(i, j int) bool {
  41. return m[res[i]] > m[res[j]]
  42. })
  43. return res[:k]
  44. }
  45. // 另一种方法 堆排序:
  46. // 建立大小为k的 小顶堆,将map统计的频次依次加入堆中。后续如果出现频次更高的元素,那就替换掉小顶堆堆顶元素即可~
  47. // 时间复杂度:O(nlogk)
  48. // 那为什么不能用大顶堆呢?
  49. // 你想啊,如果定义一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/685320
推荐阅读
相关标签
  

闽ICP备14008679号