Leetcode 刷题记录 17 —— 堆

本系列为笔者的 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,也就是上一个比当前数字小的数字
相关推荐
BD_Marathon1 小时前
【Flink】部署模式
java·数据库·flink
鼠鼠我捏,要死了捏4 小时前
深入解析Java NIO多路复用原理与性能优化实践指南
java·性能优化·nio
ningqw4 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
superlls4 小时前
(Redis)主从哨兵模式与集群模式
java·开发语言·redis
让我们一起加油好吗4 小时前
【基础算法】初识搜索:递归型枚举与回溯剪枝
c++·算法·剪枝·回溯·洛谷·搜索
郝学胜-神的一滴4 小时前
Horse3D游戏引擎研发笔记(七):在QtOpenGL环境下,使用改进的Uniform变量管理方式绘制多彩四边形
c++·3d·unity·游戏引擎·图形渲染·虚幻·unreal engine
stbomei5 小时前
基于 MATLAB 的信号处理实战:滤波、傅里叶变换与频谱分析
算法·matlab·信号处理
叫我阿柒啊6 小时前
Java全栈工程师面试实战:从基础到微服务的深度解析
java·redis·微服务·node.js·vue3·全栈开发·电商平台
2401_876221346 小时前
Reachability Query(Union-Find)
c++·算法
德先生&赛先生7 小时前
LeetCode-542. 01 矩阵
算法·leetcode·矩阵