赞
踩
给你一个整数数组
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
是数组大小。
思路:
时间复杂度:O(nlogk),n 表示数组的长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n);接着,遍历用于存储元素频率的 map,如果元素的频率大于大顶堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,这里维护堆的数目是 k,所以这一系列操作的时间复杂度是 O(nlogk) 的;因此,总的时间复杂度是 O(nlogk)。
空间复杂度: O(n),最坏情况下(每个元素都不同),map 需要存储 n 个键值对,优先队列需要存储 k 个元素,因此,空间复杂度是 O(n)。
- vector<int> topKFrequent(vector<int>& nums, int k) {
- priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // 优先级队列(大顶堆)
- unordered_map<int, int> cnt; // 负责统计每个元素出现的次数 key:元素值 value:元素出现次数
-
- // for (auto num : nums) cnt[num]++;
- for (int i = 0; i < nums.size(); i++)
- {
- cnt[nums[i]] += 1;
- }
-
- // for (auto kv : cnt) {
- // pq.push({kv.second, kv.first});
- // while (pq.size() > k) pq.pop();
- // }
- for (unordered_map<int, int>::iterator it = cnt.begin(); it != cnt.end(); it++)
- {
- pq.push({it->second, it->first}); // 根据map中的key(次数)来排序,value为次数对应的元素值
- if (pq.size() > k) // pq始终维护长度为k的队列(此处提前限定了pq长度为k,下面while不用for (int i = 0; i < k; i++) 了)
- {
- pq.pop();
- }
- }
-
- // vector<int> res;
- // while (!pq.empty())
- // {
- // res.push_back(pq.top().second);
- // pq.pop();
- // }
- // return res;
- vector<int> res;
- while (!pq.empty())
- {
- res.push_back(pq.top().second); // 从队首依次从大到小获取每个元素的second域(元素值),并存入v
- pq.pop();
- }
- return res;
- }

这道题做的快自闭了,STL的优先级队列刚开始自己写语法都有问题 = = !哎难受~
补充一波Go实现的解法,不过可能时间复杂度不满足题目要求的优于 O(n log n) ,因为排序过程也有自己的时间复杂度,且算是一种方法吧 - - !
- func topKFrequent(nums []int, k int) []int {
- // 1.利用map统计每个元素出现频次
- m := make(map[int]int)
- for _, val := range nums {
- m[val] += 1
- }
-
- // 2.将map转为结构体数组,再利用sort包按结构体的Val"从大到小"排序
- type KV struct {
- Key int
- Val int
- }
-
- kvSlice := make([]KV, 0)
- for k, v := range m {
- kv := KV{k, v}
- kvSlice = append(kvSlice, kv)
- }
-
- sort.Slice(kvSlice, func(i, j int) bool { // 对结构体的val值"从大到小"排序
- return kvSlice[i].Val > kvSlice[j].Val
- })
-
- fmt.Println(kvSlice)
-
- // 3.输出前k个高频元素
- res := make([]int, 0)
- for i := 0; i < k; i++ {
- res = append(res, kvSlice[i].Key)
- }
-
- return res
- }
-
-
- // 上述过程简化版:
- func topKFrequent(nums []int, k int) []int {
- m := make(map[int]int, 0)
-
- for i := 0; i < len(nums); i++ {
- m[nums[i]] += 1
- }
-
- // 去重后的nums数组
- res := make([]int, 0)
- for k, _ := range m {
- res = append(res, k)
- }
-
- // 根据【出现频次】对【去重后的nums数组】排序
- sort.Slice(res, func(i, j int) bool {
- return m[res[i]] > m[res[j]]
- })
-
- return res[:k]
- }
-
- // 另一种方法 堆排序:
- // 建立大小为k的 小顶堆,将map统计的频次依次加入堆中。后续如果出现频次更高的元素,那就替换掉小顶堆堆顶元素即可~
- // 时间复杂度:O(nlogk)
-
- // 那为什么不能用大顶堆呢?
- // 你想啊,如果定义一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。