347. Top K Frequent Elements

本文介绍了一种寻找整数列表中出现频率最高的k个元素的有效算法。提出两种解决方案:一是使用桶排序思想达到O(n)的时间复杂度;二是采用最大堆实现O(n*log(n-k))的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Top K Frequent Elements

原题介绍

Given a non-empty array of integers, return the k most frequent elements.

For example,
Given [1,1,1,2,2,3] and k = 2, return [1,2].

Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.
Subscribe to see which companies asked this question

给出一个非空整数列表,返回出现频次最大的k个元素

解题思路

O(n)

  1. hash,得到<元素,频次>键值对
  2. 因为频次小于n,建散列表,即新建大小为n+1的数组,数组下标为频次,数组内容为有相同频次的键值list,对散列表按下标由大到小遍历,找出k个键值

ref:https://leetcode.com/discuss/100581/java-o-n-solution-bucket-sort

O(n*log(n-k))

  1. hash map
  2. 新建大小为n-k的最大堆,遍历<元素, 频次>,频次大于堆顶的保存到返回列表,频次小于堆顶的入堆,最后最小堆保存的是出现频次最低的n-k个,被“舍弃”的元素则是出现频次最大的k个

ref:https://leetcode.com/discuss/100562/o-log-k-unordered_map-and-priority_queue-maxheap-solution

### C++ 实现 Top-K 算法 Top-K 问题是常见的算法问题之一,通常用于从大量数据中找到排名靠前的 K 个元素。下面是一个完整的 C++ 示例代码及其解释。 #### 示例代码 ```cpp #include <iostream> #include <vector> #include <queue> #include <functional> using namespace std; // 定义比较函数,使得 priority_queue 成为最小堆 struct Compare { bool operator()(const pair<int, int>& a, const pair<int, int>& b) { return a.second < b.second; // 按照频率从小到大排序 } }; vector<int> topKFrequentElements(vector<int>& nums, int k) { unordered_map<int, int> frequencyMap; // 记录每个元素出现的次数 for (auto num : nums) { frequencyMap[num]++; } // 使用优先队列(最小堆)存储频率最高的 k 个元素 priority_queue<pair<int, int>, vector<pair<int, int>>, Compare> minHeap; for (auto& entry : frequencyMap) { minHeap.push({entry.first, entry.second}); if (minHeap.size() > k) { minHeap.pop(); // 移除堆顶元素(频率最低) } } vector<int> result; while (!minHeap.empty()) { result.push_back(minHeap.top().first); // 提取堆中的元素 minHeap.pop(); } reverse(result.begin(), result.end()); // 结果按频率降序排列 return result; } int main() { vector<int> nums = {1, 1, 1, 2, 2, 3}; int k = 2; vector<int> result = topKFrequentElements(nums, k); cout << "Top-" << k << " frequent elements: "; for (auto elem : result) { cout << elem << " "; } cout << endl; return 0; } ``` --- ### 代码详解 1. **输入准备** - 输入数组 `nums` 是待处理的数据集。 - 参数 `k` 表示需要找出的最高频次的元素数量。 2. **构建频率表** - 使用 `unordered_map<int, int>` 存储每个元素的出现次数[^1]。 - 遍历整个数组,统计每个元素的频率。 3. **使用最小堆筛选 Top-K 元素** - 创建一个自定义比较规则的小根堆(`priority_queue`),其中堆顶是最不频繁的元素。 - 遍历频率表,将元素插入堆中。如果堆的大小超过了 `k`,则弹出堆顶元素(即频率最低的元素)。 4. **提取结果** - 堆中剩余的元素就是频率最高的 `k` 个元素。 - 将这些元素依次取出并存入结果向量中。 5. **输出结果** - 输出最终的 Top-K 频繁元素列表。 --- ### 性能分析 - **时间复杂度**: - 统计频率阶段:\( O(n) \),其中 \( n \) 是数组长度。 - 构建堆阶段:每次插入或删除操作的时间复杂度为 \( O(\log{k}) \),总共最多进行 \( m \) 次(m 为不同元素的数量)。因此这一部分的时间复杂度为 \( O(m \cdot \log{k}) \)[^1]。 - 整体时间复杂度为 \( O(n + m \cdot \log{k}) \)。 - **空间复杂度**: - 主要由频率表和堆占用的空间决定,分别为 \( O(m) \) 和 \( O(k) \)。整体空间复杂度为 \( O(m + k) \)。 --- ### 示例运行 假设输入数组为 `{1, 1, 1, 2, 2, 3}`,参数 `k=2`: - 频率表为:`{1 -> 3, 2 -> 2, 3 -> 1}` - 最小堆逐步变化过程: - 插入 `(1, 3)` → 堆为 `[(1, 3)]` - 插入 `(2, 2)` → 堆为 `[(2, 2), (1, 3)]` - 插入 `(3, 1)` 并移除堆顶 `(3, 1)` → 堆为 `[(2, 2), (1, 3)]` - 最终结果为 `[1, 2]`。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值