优先队列算法( Priority queue)

前言:

引入:优先队列问题常用于降低时间复杂度,达到快速搜索的目的

源码阅读Priority queue类:

在这里插入图片描述

底层分析:依据优先级构造堆

下面我们来谈一谈实现的原理

优先队列是利用堆来实现的
堆可以看做的一颗完全二叉树的顺序存储结构,(大顶堆:每个结点的值都大于等于左右孩子的值)

优先队列的两个基本操作:(都在维护堆序性
出队:堆顶出队,最后一个记录,代替堆顶的位置,重新调整为堆
入队:将新元素放入树中的末尾,再调整为堆


一个大顶堆构建完成后,且堆顶出队后,其他结点都满足最大堆的定义,只需要将堆顶执行下沉操作,
即都可再次调整为堆

出队后的操作:下沉(在已经构建好堆的基础上再次维护堆):
堆顶与左右孩子比较,若大则已经满足无需构建,若比孩子小,则与较大的孩子交换,交换到新位置后继续向下比较
(这里在堆排序里详细探讨过)
比较次数最多为树的高度
在这里插入图片描述
如果不理解上述过程建议看我的博客:堆排序


入队后的操作:上浮 (比较次数:最多为树的高度h)
入队时,将新元素放入最后一个记录之后,例如29入队,放在12的后面

在这里插入图片描述

复杂度分析:

优先队列的时间复杂度为:logn(而线性的复杂度为n,有时为二维复杂度将程指数阶,使用优先队列将大大提升效率)

这是是由于上浮和下沉中最大遍历范围为树的高度,完全二叉树的树高为h=[logn]+1

Lambda表达式构建Priority queue

在这里插入图片描述
这里我们只需要
PriorityQueue queue =new PriorityQueue(大小,比较器类型);

  1. Lambda表达式法书写比较器接口
    Lambda表达式允许通过表达式来代替功能接口功能(注意表达式中参数以及参数方法的一致性,使用中尽量添加泛型约束)

Lambda作为排序参口传递进方法中,也就是说Lambda可以替代Comparator接口作为排序参数

Lambda表达式的基本语法:
( p a r a m e t e r s ) − > e x p r e s s i o n 或 ( p a r a m e t e r s ) − > s t a t e m e n t s ; (parameters) -> expression 或 (parameters) ->{ statements; } (parameters)>expression(parameters)>statements;

// 1. 不需要参数,返回值为 5  
() -> 5  
 
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
 
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
 
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
 
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s) 

那么我们如何实现以小顶堆进行排序的优先级队列呢

 PriorityQueue heap=new PriorityQueue<Integer>((n1,n2)->n1-n2);

(这里的泛型最好加上)
解释:参数为两个数,返回两个值的差 差为1 则交换,差为负数和0不交换,维护了升序排列

例题实现:

  1. 找出第 K 大的异或坐标值
    给你一个二维矩阵 matrix 和一个整数 k ,矩阵大小为 m x n 由非负整数组成。

矩阵中坐标 (a, b) 的 值 可由对所有满足 0 <= i <= a < m 且 0 <= j <= b < n 的元素 matrix[i][j](下标从 0 开始计数)执行异或运算得到。

请你找出 matrix 的所有坐标中第 k 大的值(k 的值从 1 开始计数)。

示例 1:

输入:matrix = [[5,2],[1,6]], k = 1
输出:7
解释:坐标 (0,1) 的值是 5 XOR 2 = 7 ,为最大的值。
示例 2:

输入:matrix = [[5,2],[1,6]], k = 2
输出:5
解释:坐标 (0,0) 的值是 5 = 5 ,为第 2 大的值。
示例 3:

输入:matrix = [[5,2],[1,6]], k = 3
输出:4
解释:坐标 (1,0) 的值是 5 XOR 1 = 4 ,为第 3 大的值。
示例 4:

输入:matrix = [[5,2],[1,6]], k = 4
输出:0
解释:坐标 (1,1) 的值是 5 XOR 2 XOR 1 XOR 6 = 0 ,为第 4 大的值。

思路:首先思路还是前缀和,但是这次变成了二维前缀
通过模拟后我们得到前缀公式(这里的前缀和做出了修改,与行和列有关)

s u m [ i ] [ j ] = s u m [ i − 1 ] [ j ] 异 或 s u m [ i ] [ j − 1 ] 异 或 s u m [ i − 1 ] [ j − 1 ] 异 或 m a t [ i − 1 ] [ j − 1 ] ; sum[i][j] = sum[i - 1][j] 异或sum[i][j - 1] 异或 sum[i - 1][j - 1] 异或 mat[i - 1][j - 1]; sum[i][j]=sum[i1][j]sum[i][j1]sum[i1][j1]mat[i1][j1];

思路:得到前缀和后,这时我们构建一个由小顶堆构成的大小为k的优先队列,并在加入的过程中不断维护优先队列的大小
这样,当我们加入完所有元素后,只有k-1个元素比我们堆顶的元素大,那么堆顶元素即为第k大的元素

代码实现:

class Solution {
    public int kthLargestValue(int[][] mat, int k) {
        int m = mat.length, n = mat[0].length;
        int[][] sum = new int[m + 1][n + 1];
        PriorityQueue<Integer> q = new PriorityQueue<>(k, (a, b)->a - b);
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                sum[i][j] = sum[i - 1][j] ^ sum[i][j - 1] ^ sum[i - 1][j - 1] ^ mat[i - 1][j - 1];
                if (q.size() < k) {//容量小于k,则加入
                    q.add(sum[i][j]);
                } else {
                    if (sum[i][j] > q.peek()) {//比堆顶元素大,加入进堆中并维护
                        q.poll();
                        q.add(sum[i][j]);
                    }
                }
            }
        }
        return q.peek();
    }
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值