快速排序法博客:
https://blog.youkuaiyun.com/u014241071/article/details/81565148
题目
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
- 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
- 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
- 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
- 你可以按任意顺序返回答案。
官方题解
方法一:堆
思路与算法
首先遍历整个数组,并使用哈希表记录每个数字出现的次数,并形成一个「出现次数数组」。找出原数组的前 k 个高频元素,就相当于找出「出现次数数组」的前 k 大的值。
最简单的做法是给「出现次数数组」排序。但由于可能有 O(N) 个不同的出现次数(其中 N 为原数组长度),故总的算法复杂度会达到 O(N log N),不满足题目的要求。
在这里,我们可以利用堆的思想:建立一个小顶堆,然后遍历「出现次数数组」:
- 如果堆的元素个数小于 k,就可以直接插入堆中。
- 如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有 k 个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中。
遍历完成后,堆中的元素就代表了「出现次数数组」中前 k 大的值。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
for (int num : nums) {
occurrences.put(num, occurrences.getOrDefault(num, 0) + 1);
}
// int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] m, int[] n) {
return m[1] - n[1];
}
});
for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
int num = entry.getKey(), count = entry.getValue();
if (queue.size() == k) {
if (queue.peek()[1] < count) {
queue.poll();
queue.offer(new int[]{num, count});
}
} else {
queue.offer(new int[]{num, count});
}
}
int[] ret = new int[k];
for (int i = 0; i < k; ++i) {
ret[i] = queue.poll()[0];
}
return ret;
}
}
方法二:基于快速排序
思路与算法
我们可以使用基于快速排序的方法,求出「出现次数数组」的前 k 大的值。
在对数组 arr[l…r] 做快速排序的过程中,我们首先将数组划分为两个部分 arr[i…q−1] 与 arr[q+1…j],并使得 arr[i…q−1] 中的每一个值都不超过 arr[q],且 arr[q+1…j] 中的每一个值都大于 arr[q]。
于是,我们根据 k 与左侧子数组 arr[i…q−1] 的长度(为 q−i)的大小关系:
- 如果 k≤q−i,则数组 arr[l…r] 前 k 大的值,就等于子数组 arr[i…q−1] 前 kk 大的值。
- 否则,数组 arr[l…r] 前 k 大的值,就等于左侧子数组全部元素,加上右侧子数组 arr[q+1…j] 中前 k−(q−i) 大的值。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
for (int num : nums) {
occurrences.put(num, occurrences.getOrDefault(num, 0) + 1);
}
List<int[]> values = new ArrayList<int[]>();
for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
int num = entry.getKey(), count = entry.getValue();
values.add(new int[]{num, count});
}
int[] ret = new int[k];
qsort(values, 0, values.size() - 1, ret, 0, k);
return ret;
}
public void qsort(List<int[]> values, int start, int end, int[] ret, int retIndex, int k) {
int picked = (int) (Math.random() * (end - start + 1)) + start;
Collections.swap(values, picked, start);
int pivot = values.get(start)[1];
int index = start;
for (int i = start + 1; i <= end; i++) {
if (values.get(i)[1] >= pivot) {
Collections.swap(values, index + 1, i);
index++;
}
}
Collections.swap(values, start, index);
if (k <= index - start) {
qsort(values, start, index - 1, ret, retIndex, k);
} else {
for (int i = start; i <= index; i++) {
ret[retIndex++] = values.get(i)[0];
}
if (k > index - start + 1) {
qsort(values, index + 1, end, ret, retIndex, k - (index - start + 1));
}
}
}
}
总结:快速排序的这个方法没看懂。官方的这个思路描述的实在是不像是人话,理解不明白。硬着头皮一段一段思考代码起码还能稍微理解一下,看官方的文字描述是真的折磨。
还有一个很好的思路。
作者:yuan-shan-mei
https://leetcode-cn.com/problems/top-k-frequent-elements/solution/zui-xiao-dui-fa-by-yuan-shan-mei-fnku/
最小堆法
思路:
①借助哈希表来建立数字及其出现频次的映射
②维护一个元素数目为k的最小堆
③每次都将新元素与堆顶元素(堆中频率最小的元素)比较
④若新的元素比堆顶端的元素大,则弹出堆顶元素,将新元素添加进去
⑤最终,堆中k个元素即为前k个高频元素
class Solution{
public List<Integer> topKFrequent(int[] nums,int k){
//使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
HashMap<Integer,Integer> map=new HashMap();
for(int num:nums){
if(map.containsKey(num)){
//若已存在,值加一
map.put(num,map.get(num)+1);
}else{
//若不存在,创建新的键值对
map.put(num,1);
}
}
}
//遍历map,用优先队列最小堆保存频率最大的k个元素
//建立小顶堆,若想建立大顶堆,则return map.get(b)-map.get(a)
/*继承Comparator接口,必须重写compare 方法*/
PriorityQueue<Integer> pq=new PriorityQueue<>(new Comparator<Integer>(){
@Override
public int compare(Integer a,Integer b){
return map.get(a)-map.get(b);
}
});
//保存频率最大的k个元素,peek()方法:查看堆顶元素值
for(Integer key:map.keySet()){
if(pq.size()<k){
pq.add(key);
}else if(map.get(key)>map.get(pq.peek())){
pq.remove();
pq.add(key);
}
}
//取出最小堆的元素
List<Integer> res=new ArrayList<>();
while(!pq.isEmpty()){
res.add(pq.remove);
}
return res;
}
本文介绍了一种高效算法,用于找出数组中出现频率最高的k个元素。通过使用哈希表统计频次,结合快速排序思想优化查找过程,实现优于O(nlogn)的时间复杂度。
1083

被折叠的 条评论
为什么被折叠?



