本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。
01 数组中的第K个最大元素

方法一:快速排序 + 二分递归
java
class Solution {
public int myFunction(int[] nums, int l, int r, int k){
if(l == r){
return nums[k];
}
int x = nums[l]; //标杆元素
int i = l - 1, j = r + 1; //双指针
while(i < j){
do i++; while(nums[i] < x);
do j--; while(nums[j] > x);
if(i < j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
/**
j 为标杆,左侧为nums[l...j],右侧为nums[j+1...r]
*/
if(k <= j){
return myFunction(nums, l, j, k);
}else{
return myFunction(nums, j+1, r, k);
}
}
public int findKthLargest(int[] nums, int k) {
int n = nums.length;
return myFunction(nums, 0, n-1, n-k);
}
}
02 前K个高频元素

方法一:小根堆
java
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for(int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
public int compare(int[] m, int[] n){
return m[1] - n[1];
}
});
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
int num = entry.getKey();
int 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;
}
}
03 数据流的中位数


java
class MedianFinder {
public MedianFinder() {
}
public void addNum(int num) {
}
public double findMedian() {
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
方法一:大顶堆 + 小顶堆
左边大顶堆,右边小顶堆;
小的加左边,大的加右边;
平衡俩堆数,新加就弹出,堆顶给对家;
奇数取多的,偶数取除2
;
java
class MedianFinder {
PriorityQueue<Integer> minHeap;
PriorityQueue<Integer> maxHeap;
public MedianFinder() {
minHeap = new PriorityQueue<Integer>(); // 小顶堆
maxHeap = new PriorityQueue<Integer>((x, y) -> y - x);// 大顶堆
}
public void addNum(int num) {
if (minHeap.isEmpty() || num > minHeap.peek()) {
minHeap.offer(num);
} else {
maxHeap.offer(num);
}
if (minHeap.size() > maxHeap.size() + 1) {
maxHeap.offer(minHeap.poll());
} else if (maxHeap.size() > minHeap.size()) {
minHeap.offer(maxHeap.poll());
}
}
public double findMedian() {
if (minHeap.size() == maxHeap.size()) {
return (minHeap.peek() + maxHeap.peek()) / 2.0;
} else {
return minHeap.peek();
}
}
}
蓝猫淘气三千问
①(x, y) -> y - x
是啥意思?
这个写法是 Java 里的Lambda表达式 ,用于实现一个匿名的比较器(Comparator),PriorityQueue
默认是小顶堆 ,即堆顶是最小元素,我们希望实现的是大顶堆 ,即堆顶是最大元素。x - y
实现默认的小顶堆排序,数字小的优先,y - x
交换了顺序,实现反向排序,数字大优先。
②if (minHeap.isEmpty() || num > minHeap.peek())
为什么这样判断?
如果 minHeap
为空(比如刚开始没数据),直接放进 minHeap
,这样做方便后面取中位数,如果 num
比当前minHeap
堆顶的数(即较大一半中最小的数)还大,说明num
属于数字序列的后半部分,应该放入存放大数的minHeap
,否则,num
更小,属于前半部分,放到maxHeap
。
③ return minHeap.peek();
为什么能直接返回?
代码中是保证 minHeap.size() >= maxHeap.size()
,且 minHeap
最大比 maxHeap
多1个元素,当元素个数为奇数时,额外的那个元素存在 minHeap
中。
方法二:有序集合 + 双指针
java
class MedianFinder {
TreeMap<Integer, Integer> nums;
int n;
int[] left;
int[] right;
public MedianFinder() {
nums = new TreeMap<Integer, Integer>();
n = 0;
left = new int[2];
right = new int[2];
}
public void addNum(int num) {
nums.put(num, nums.getOrDefault(num, 0) + 1);
if (n == 0) {
left[0] = right[0] = num;
left[1] = right[1] = 1;
} else if ((n & 1) != 0) {
if (num < left[0]) {
decrease(left);
} else {
increase(right);
}
} else {
if (num > left[0] && num < right[0]) {
increase(left);
decrease(right);
} else if (num >= right[0]) {
increase(left);
} else {
decrease(right);
System.arraycopy(right, 0, left, 0, 2);
}
}
n++;
}
public double findMedian() {
return (left[0] + right[0]) / 2.0;
}
private void increase(int[] iterator) {
iterator[1]++;
if (iterator[1] > nums.get(iterator[0])) {
iterator[0] = nums.ceilingKey(iterator[0] + 1);
iterator[1] = 1;
}
}
private void decrease(int[] iterator) {
iterator[1]--;
if (iterator[1] == 0) {
iterator[0] = nums.floorKey(iterator[0] - 1);
iterator[1] = nums.get(iterator[0]);
}
}
}
代码 | 含义 |
---|---|
System.arraycopy(right, 0, left, 0, 2); |
把right数组的两个元素完全复制给left数组 |
nums.ceilingKey(iterator[0] + 1); |
找到TreeMap 中 >= 当前数字 + 1 的最小key,也就是下一个比当前数字大的数字 |
nums.floorKey(iterator[0] - 1); |
找到TreeMap 中 <= 当前数字 - 1 的最大key,也就是上一个比当前数字小的数字 |