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,也就是上一个比当前数字小的数字
相关推荐
sg_knight2 小时前
Spring Cloud LoadBalancer深度解析:官方负载均衡方案迁移指南与避坑实践
java·spring boot·spring·spring cloud·微服务·负载均衡
_何同学3 小时前
Ollama 安装 DeepSeek 与 Spring Boot 集成指南
java·spring boot·后端·ai
圣保罗的大教堂3 小时前
leetcode 2566. 替换一个数字后的最大差值 简单
leetcode
剪一朵云爱着3 小时前
力扣面试题 17.05. 字母与数字
算法·leetcode
虾球xz4 小时前
CppCon 2016 学习:GAME ENGINE USING C++11
大数据·开发语言·c++·学习
虾球xz4 小时前
CppCon 2016 学习:fixed_point Library
开发语言·c++·学习
Code季风4 小时前
跨语言RPC:使用Java客户端调用Go服务端的HTTP-RPC服务
java·网络协议·http·rpc·golang
盖世英雄酱581364 小时前
时间设置的是23点59分59秒,数据库却存的是第二天00:00:00
java·数据库·后端
code喵喵4 小时前
八种数据结构简介
数据结构·算法·推荐算法
HaiQinyanAN4 小时前
【学习笔记】nlohmannjson&&cjson
c++·笔记·学习·json