堆和优先队列

本文详细介绍了使用Java中的优先队列(堆)解决寻找数组中第K大元素、前K个高频元素、数据流中的第K大元素以及最接近原点的K个点等问题。通过实例代码展示了如何利用小顶堆和大顶堆,结合哈希表和lambda表达式进行高效求解。此外,还讨论了如何在堆中维护元素个数以输出所需结果。
摘要由CSDN通过智能技术生成


首先要了解什么是堆,大顶堆,小顶堆,优先队列的概念。
https://www.cnblogs.com/lanhaicode/p/10546257.html
Java中的优先队列默认是小顶堆形式

PriorityQueue<Integer> pq = new PriorityQueue<>();

若要改成大顶堆,则需要lambada表达式

PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> {return b -a;});

优先队列常常用来解决 top k 的问题

215. 数组中的第K个最大元素(优先队列)

注意:本题还可以使用快速排序的分区思想,这里先不做介绍,等排序章节再总结。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        /**
        分析:
        典型的求解top k 问题,应该想到优先队列(堆这种数据结构)
         */
         // 排序法解题,面试等通知吧
        // Arrays.sort(nums);
        // return nums[nums.length - k];

        // 使用优先队列(默认是小顶堆,这里使用lambda表达式改成大顶堆)
        PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> {return b -a;});
        // 储存到堆内
        for(int i = 0; i < nums.length; i++){
            pq.add(nums[i]);
        }
        // 取出前k - 1个
        for(int i = 0; i < k - 1; i++){
            pq.remove();
        }
        // 第k大元素
        return pq.peek();

    }
}

347. 前 K 个高频元素(哈希表+优先队列)

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 计算频率 使用哈希表
        // 取前k个  使用优先队列(内部使用堆结构)
        Map<Integer,Integer> map = new HashMap<>();
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0) + 1);
        }
        // 定义优先队列
        Queue<Integer> pq = new PriorityQueue<>((o1,o2) -> map.get(o2) - map.get(o1));
        // 储存所有的键
        pq.addAll(map.keySet());
        int[] res = new int[k];
        // 取前k个
        for(int i = 0; i < k; i++){
            res[i] = pq.remove();
        }
        return res;
    }
}

703. 数据流中的第 K 大元素(小顶堆+维护k个容量=堆顶是第k大值)

class KthLargest {
    /**
    分析:
    这是一个设计题,但是还是求 top k的问题
     */
    private int k;
    // private int[] nums;
    private PriorityQueue<Integer> pq ;
    public KthLargest(int k, int[] nums) {
        // 初始化
        this.k = k;
        // 小顶堆的大小限制为k个,堆顶值刚好就是第k大的元素
        pq = new PriorityQueue<>(k);
        // 添加元素
        for(int num:nums){
            add(num);
        }
    }
    
    public int add(int val) {
        // 优先队列内容量不足 k个 直接添加val
        // 这个小顶堆 就是一直在维护容量为k的堆,堆顶值就是第k大元素
        if(pq.size() < k){
            pq.add(val);
        }else if(val > pq.peek()){
            // 新加元素和堆顶比较 比堆顶元素小则丢弃,否则删除堆顶元素,插入新元素
            pq.remove();
            pq.add(val);
        }
        return pq.peek();

    }
}

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest obj = new KthLargest(k, nums);
 * int param_1 = obj.add(val);
 */

973. 最接近原点的 K 个点(大顶堆+维护k个容量=超过k则移除堆顶,余下就是k个最小值)

class Solution {
    // 本题是求前 K 小,因此用一个容量为 K 的大根堆,每次 remove 出最大的数,那堆中保留的就是前 K 小啦!
    // 大顶堆
    public int[][] kClosest(int[][] points, int K) {
        PriorityQueue<int[]> pq
        //  关于二维数组如何重写lambda表达式?
                = new PriorityQueue<>(K , (a, b) -> (b[0] * b[0] + b[1] * b[1]) - (a[0] * a[0] + a[1] * a[1]));

        for (int[] point : points) {
            // 添加到大根堆中
            pq.add(point);
            // 堆内容量超过k个,那么移除堆顶,余下的k个元素就是 k个最小值
            if (pq.size() > K) pq.remove();
        }

        // 堆中存储的就是出现了前 k 个高频的元素(最小值)
        int[][] res = new int[K][2];
        int index = 0;
        while (!pq.isEmpty()) {
            res[index++] = pq.remove();
        }
        return res;
    }
}

总结703和973,典型的top k 问题,但是用到了一些数学的技巧,维护堆内的元素个数。输出想要的结果。常见操作如703,小顶堆+维护k个=堆顶就是第k大元素。又如973,大顶堆+维护k个=超过k个就移除堆顶,余下的k个就是最小值组成的

其中还学习了lambda表达式。

普通数:实际上是 (int a,int b)
((a,b) -> {return a - b;});  这是升序排序
((a,b) -> {return b - a;});  这是降序排序
一维数组:实际上是 (int []a,int []b)
((a, b) ->  a[0] * a[0] + a[1] * a[1]) - (b[0] * b[0] + b[1] * b[1]));  按照离原点距离升序排序
((a, b) ->  b[0] * b[0] + b[1] * b[1]) - (a[0] * a[0] + a[1] * a[1]));  按照离原点距离降序排序
二维数组:实际上是 (int [][]a,int [][]b)
((a,b) -> a[0] - b[0]); 按照二维数组中一维数组第一个数升序排序
((a,b) -> b[0] - a[0]); 按照二维数组中一维数组第一个数降序排序

((a,b) -> a[1] - b[1]); 按照二维数组中一维数组第二个数升序排序
((a,b) -> b[1] - a[1]); 按照二维数组中一维数组第二个数降序排序

观察以上柿子可以知道,若定义变量 (a,b),这里不管是一维还是二维,只要后续是a在前就是 升序,反之降序。
对于二维数组也很好理解 (int [][] a,int [][] b) 那么a[0] 就是一维数组
在这里插入图片描述
在这里插入图片描述

1046. 最后一块石头的重量(大顶堆)

class Solution {
    public int lastStoneWeight(int[] stones) {
        /**
        分析:
        有点像开心消消乐,本质上还是top k的问题
         */
         PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> b - a);
         for(int weight:stones){
             pq.add(weight);
         }
         while(pq.size() > 1){
             int y = pq.remove();
             int x = pq.remove();
             if( x != y){
                 pq.add( y - x);
             }
         }
         // 大顶堆非空 才输出
         if(!pq.isEmpty())
            return pq.peek();
        // 都被粉碎了 就返回 0
        return 0;

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值