文章目录
- [1. 最后一块石头的重量(LC1046)](#1. 最后一块石头的重量(LC1046))
- [2. 数据流中的第K大元素(LC703)](#2. 数据流中的第K大元素(LC703))
- [3. 前K个高频单词(LC692)](#3. 前K个高频单词(LC692))
- [4. 数据流中的中位数(LC295)](#4. 数据流中的中位数(LC295))
TOP - K问题主要有两种比较优秀的解法:
- 堆 时间复杂度:O(n * log2n)
- 快速选择算法 时间复杂度O(n) ,空间开销比较大
1. 最后一块石头的重量(LC1046)
题目描述

解题思路
利用大根堆模拟这个过程,每次取出前两个大的元素碰撞,得到的结果再入队.
代码实现
java
class Solution {
public int lastStoneWeight(int[] stones) {
PriorityQueue<Integer> heap = new PriorityQueue<>((a,b)->b-a);
//入队
for (int stone : stones)
heap.offer(stone);
while(heap.size()>1){
int s1 = heap.poll();
int s2 = heap.poll();
if(s1!=s2)
heap.offer(s1-s2);
}
return heap.isEmpty()?0:heap.poll();
}
}
2. 数据流中的第K大元素(LC703)
题目描述

解题思路
- 创建大小为K的小根堆
- 循环,元素依次进堆,判断堆的大小是否超过k,如果超过则令堆顶元素出堆。
代码实现
java
class KthLargest {
int k;
int[] nums;
PriorityQueue<Integer> heap = new PriorityQueue<>();
public KthLargest(int k, int[] nums) {
this.k = k;
this.nums = nums;
for(int x:nums){
heap.offer(x);
if(heap.size()>k)
heap.poll();
}
}
public int add(int val) {
heap.offer(val);
if(heap.size()>k)
heap.poll();
return heap.peek();
}
}
3. 前K个高频单词(LC692)
题目描述

解题思路
利用堆来解决。
- 利用哈希表统计单词对应的次数
- 创建大小为k的堆
- 对于频次,应该创建小根堆
- 对于字典序(频次相同时),应该创建大根堆,让序列靠后的单词先出堆。
- 循环,让元素依次进堆,判断
- 提取结果,小根堆依次提取的结果是从小到大的,可以逆序存放在结果数组中,也可以调用
reverse方法
代码实现
java
class Solution {
public List<String> topKFrequent(String[] words, int k) {
List<String> ret = new ArrayList<>();
HashMap<String,Integer> hash = new HashMap<>();
//预处理,统计频次
for(String word:words){
if(hash.containsKey(word))
hash.put(word,hash.get(word)+1);
else
hash.put(word,1);
}
//创建小根堆
PriorityQueue<Map.Entry<String,Integer>> heap = new PriorityQueue<>((o1,o2) ->{
if(o1.getValue().equals(o2.getValue()))
return o2.getKey().compareTo(o1.getKey());
else
return o1.getValue() - o2.getValue();
});
//循环入队
for(Map.Entry<String,Integer> entry:hash.entrySet()){
heap.offer(entry);
if(heap.size()>k)
heap.poll();
}
//提取结果
while(!heap.isEmpty())
ret.add(heap.poll().getKey());
Collections.reverse(ret);
return ret;
}
}
4. 数据流中的中位数(LC295)
题目描述

解题思路
- 利用大小堆来解决,创建大根堆存放左半边的元素,数量为m,小根堆存放右半边的元素,数量为n,保持
m==n或m==n+1。m==n时,分别取两个堆的堆顶元素,求平均值;m==n+1时,取大根堆的堆顶元素就是中位数。 addNum: 分类讨论,要保证m==n或m==n+1m==n时,取大根堆堆顶元素x- num<x 或 大根堆为空时,x添加到大根堆
- 否则 添加到小根堆中,但是此时右边比左边数量多,取出堆顶元素y,放入小根堆中
m==n+1- num>x,直接放入右边
- 否则先把x放入左边,x出堆放入右边。
代码实现
java
class MedianFinder {
PriorityQueue<Integer> left = new PriorityQueue<>((a,b)->b-a);
PriorityQueue<Integer> right = new PriorityQueue<>();
public MedianFinder() {
}
public void addNum(int num) {
if(left.size()==right.size()){
if(left.isEmpty() || left.peek()>=num)
left.offer(num);
else{
right.offer(num);
left.offer(right.poll());
}
}else{
if(left.peek()<=num)
right.offer(num);
else{
left.offer(num);
right.offer(left.poll());
}
}
}
public double findMedian() {
if(left.size()>right.size())
return left.peek();
else
return (left.peek()+right.peek())/2.0 ;
}
}