文章目录
-
- 一、题目描述
- 二、题目分析与思路导图
- 三、解法一:排序法(简单直接)
-
- [1. 原理说明](#1. 原理说明)
- [2. 流程图](#2. 流程图)
- [3. 时间复杂度与空间复杂度](#3. 时间复杂度与空间复杂度)
- [4. Java代码实现](#4. Java代码实现)
- 四、解法二:最小堆法(推荐做法)
-
- [1. 原理说明](#1. 原理说明)
- [2. 时序图](#2. 时序图)
- [3. 时间复杂度与空间复杂度](#3. 时间复杂度与空间复杂度)
- [4. Java代码实现](#4. Java代码实现)
- 五、解法三:桶排序法(最优解)
-
- [1. 原理说明](#1. 原理说明)
- [2. 时间复杂度与空间复杂度](#2. 时间复杂度与空间复杂度)
- [3. Java代码实现](#3. Java代码实现)
- 六、总结与对比表
一、题目描述
给定一个整数数组 nums 和一个整数 k,请返回其中出现频率前 k 高的元素。
你可以按任意顺序返回答案。
示例:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
二、题目分析与思路导图
这道题的核心在于 统计元素出现的频率 ,然后找到出现次数最多的 k 个元素。
Top K Frequent Elements
统计频率
使用HashMap
键:元素
值:出现次数
找出前K高频
排序法
小顶堆法
桶排序法
三、解法一:排序法(简单直接)
1. 原理说明
- 首先遍历数组,使用
HashMap统计每个数字的出现次数。 - 再将
Map中的键值对按照出现次数降序排序。 - 取出前
k个元素的键即可。
2. 流程图
开始
使用HashMap统计频率
转换为List>
按value降序排序
取前k个entry的key
返回结果数组
3. 时间复杂度与空间复杂度
- 时间复杂度:O(N log N),排序占主导。
- 空间复杂度:O(N),用于存储频率映射。
4. Java代码实现
java
import java.util.*;
public class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> freqMap = new HashMap<>();
for (int num : nums) {
freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
}
List<Map.Entry<Integer, Integer>> entries = new ArrayList<>(freqMap.entrySet());
entries.sort((a, b) -> b.getValue() - a.getValue());
int[] res = new int[k];
for (int i = 0; i < k; i++) {
res[i] = entries.get(i).getKey();
}
return res;
}
}
四、解法二:最小堆法(推荐做法)
1. 原理说明
利用最小堆(PriorityQueue)的性质,维持大小为 k 的堆结构:
- 遍历所有元素的频率;
- 当堆的大小小于
k时将元素加入; - 当堆的大小等于
k且当前元素的频率大于堆顶频率时,弹出堆顶并添加当前元素; - 最后堆中的元素即为出现频率前
k的元素。
2. 时序图
提取结果 小顶堆操作 建立频率表 输入nums和k 提取结果 小顶堆操作 建立频率表 输入nums和k 遍历nums统计频率 对每个元素频率进行入堆判断 保持堆大小不超过k 输出堆中所有元素
3. 时间复杂度与空间复杂度
- 时间复杂度:O(N log K),因为堆操作上限为 K。
- 空间复杂度:O(N),用于哈希表和堆存储。
4. Java代码实现
java
import java.util.*;
public class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> freqMap = new HashMap<>();
for (int num : nums) {
freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
}
PriorityQueue<Map.Entry<Integer, Integer>> heap =
new PriorityQueue<>(Comparator.comparingInt(Map.Entry::getValue));
for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()) {
heap.offer(entry);
if (heap.size() > k) {
heap.poll();
}
}
int[] res = new int[k];
for (int i = k - 1; i >= 0; i--) {
res[i] = heap.poll().getKey();
}
return res;
}
}
五、解法三:桶排序法(最优解)
1. 原理说明
桶排序法充分利用频率的离散性:
- 统计频率;
- 构建一个桶列表,其中桶索引表示频率,桶内容存储出现该频率的数字;
- 从高频桶往下搜索,直到收集够
k个元素。
2. 时间复杂度与空间复杂度
- 时间复杂度:O(N),因为每个元素都仅遍历一次。
- 空间复杂度:O(N),用于存储桶。
3. Java代码实现
java
import java.util.*;
public class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> freqMap = new HashMap<>();
for (int num : nums) {
freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
}
List<List<Integer>> buckets = new ArrayList<>();
for (int i = 0; i <= nums.length; i++) {
buckets.add(new ArrayList<>());
}
for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()) {
int freq = entry.getValue();
buckets.get(freq).add(entry.getKey());
}
List<Integer> resList = new ArrayList<>();
for (int i = buckets.size() - 1; i >= 0 && resList.size() < k; i--) {
resList.addAll(buckets.get(i));
}
int[] res = new int[k];
for (int i = 0; i < k; i++) {
res[i] = resList.get(i);
}
return res;
}
}
六、总结与对比表
| 解法 | 核心思想 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|---|
| 排序法 | 哈希 + 排序 | O(N log N) | O(N) | 数据量较小 |
| 小顶堆法 | 哈希 + 最小堆 | O(N log K) | O(N) | 高频前K问题通用 |
| 桶排序法 | 哈希 + 桶分布 | O(N) | O(N) | 频率有限且整数集合适用 |