题目:
题解:
215. 数组中的第K个最大元素
什么是Java优先级队列(Priority Queue)?
1. 题解一:最小堆
2. 题解二:桶排序法(首选)
本方法与传统的桶排序不太一样;
1. 传统桶排序是根据元素值排序,方法是以元素值为索引,将元素数量存入桶数组(即桶标号 = 元素值,桶内值 = 元素数量)。
一个桶仅有一个桶标号和一个桶内值,桶 = 存整形的数组。
2. 本题是根据元素数量排序,方法是以元素数量为索引,将元素值存入桶数组中(即桶标号 = 元素数量,桶内值 = 元素值)。
这就导致某些桶内存在多个值,桶 = 存集合的数组。
代码:
1. 代码一:最小堆
import java.util.*;
public class code347 {
// 解法1:最小堆
public static int[] topKFrequent(int[] nums, int k) {
// 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++)
{
if(map.containsKey(nums[i]))
{
map.put(nums[i], map.get(nums[i]) + 1);
}
else
{
map.put(nums[i], 1);
}
}
// 遍历map,用最小堆保存频率最大的k个元素
PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> map.get(a) - map.get(b)); // 要比较的数是map中的value而不是key值
for(Integer key: map.keySet())
{
if(queue.size() < k)
{
queue.offer(key);
}
else if(map.get(key) > map.get(queue.peek()))
{
queue.poll();
queue.offer(key);
}
}
// 取出最小堆中的元素
int res[] = new int[k];
for(int i = 0; i < k; i++)
{
res[i] = queue.poll();
}
return res;
}
public static void main(String[] args) {
int nums[] = { 4, 1, -1, 2, -1, 2, 3 };
int k = 2;
int res[] = topKFrequent(nums, k);
for(int i = 0; i < res.length; i++)
{
System.out.print(res[i] + " ");
}
}
}
2. 代码二:桶排序法(首选)
import java.util.*;
public class code347 {
// 解法2: 桶排序法
// 基于桶排序求解「前 K 个高频元素」
public static int[] topKFrequent(int[] nums, int k) {
// 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++)
{
if(map.containsKey(nums[i]))
{
map.put(nums[i], map.get(nums[i]) + 1);
}
else
{
map.put(nums[i], 1);
}
}
//桶排序
//将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
List<Integer>[] list = new List[nums.length+1];
for(int key: map.keySet())
{
// 获取出现的次数作为下标
int i = map.get(key);
if(list[i] == null)
{
list[i] = new ArrayList<>();
}
list[i].add(key);
}
// 倒序遍历数组获取出现顺序从大到小的排列
List<Integer> res = new ArrayList<>();
for(int i = list.length - 1; i >= 0 && res.size() < k; i--)
{
if(list[i] == null)
{
continue;
}
res.addAll(list[i]);
}
// list转换为数组
int ans[] = new int[res.size()];
for(int i = 0; i < res.size(); i++)
{
ans[i] = res.get(i);
}
return ans;
}
public static void main(String[] args) {
int nums[] = { 4, 1, -1, 2, -1, 2, 3 };
int k = 2;
int res[] = topKFrequent(nums, k);
for(int i = 0; i < res.length; i++)
{
System.out.print(res[i] + " ");
}
}
}
参考:
- 347. 前 K 个高频元素
- C++简洁代码(多种方法):
- c++ 小根堆or大根堆
- 347. 前 K 个高频元素 C++
- python 自己实现堆,优先队列
- 347. 前 K 个高频元素(java)
- 347. 前 K 个高频元素(Java)
技术点:
- 遍历哈希表:
for (Integer key: map.keySet())
- 哈希表计数:
//方式一
for(int num: nums)
{
if (map.containsKey(num))
{
map.put(num, map.get(num) + 1);
}
else
{
map.put(num, 1);
}
}
//方式二
for(int num: nums)
{
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
}
其中,
map.getOrDefault(key, default);
代表:
1. 如果存在key,返回key对应的value值;
2. 如果不存在key,返回default值;
- 该题堆的排序规则
//默认是最小堆,但该题最小堆并不是用map的key值来排序,而是用map的value值排序,存储的是map的key值
PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> (map.get(a) - map.get(b)));
- 第二部分的集合数组可以有多重类型,且注意集合数组创建格式
List<Integer>[] bucket = new List[nums.length+1];
ArrayList<Integer>[] bucket = new ArrayList[nums.length+1]; //可以替换
LinkedList<Integer>[] bucket = new LinkedList[nums.length+1]; //不能替换,除非将buket中new成LinkedList
- Java List.addAll()方法
ans.addAll(bucket[i]);
是将bucket[i]中存储的所有元素(bucket是个集合)全部加入ans集合中