有序矩阵中第k小的元素

Kth Smallest Element in a Sorted Matrix

问题:

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

Example:

matrix = [
   [ 1,  5,  9],
   [10, 11, 13],
   [12, 13, 15]
],
k = 8,
return 13.

Note: 
You may assume k is always valid, 1 ≤ k ≤ n^2.

解决:

① 使用最大堆,然后遍历数组每一个元素,将其加入堆,根据最大堆的性质,大的元素会排到最前面,然后我们看当前堆中的元素个数是否大于k,大于的话就将首元素去掉,循环结束后我们返回堆中的首元素即为所求。采用优先队列,自定义比较规则,使得最小元素排在队首。

public class Solution { //25ms
    //求矩阵中第k小的元素
    public static int kthSmallest(int[][] matrix,int k){
        int rows = matrix.length; //矩阵的行数
        int cols = matrix[0].length;//列数
        //优先队列
        PriorityQueue<Tuple> queue = new PriorityQueue<Tuple>(); //入队列
        /**
         * 这里有一个设计非常巧妙的地方,将第一行升序的序列先入队列,根据Tuple类型的定义,最小的元素将会在队首。
         * 每当队首元素出队列后,都将其所在列的下一个元素入队列,直到最后一行;
         * 这样,就可以比较一个元素的同一行与同一列中相邻的元素中哪个较小,从而保证队首元素始终是矩阵中所有元素的最小值
         */
        for(int j = 0; j < cols; ++ j)
            queue.offer(new Tuple(0,j,matrix[0][j]));
        //进行k-1次出队、入队,最后队首元素一定是所有元素中的第k小
        for(int i = 0; i < k - 1; ++ i){
            Tuple tmp = queue.poll(); //出队列
            if(tmp.x == rows - 1) //若是最后,则没有元素需要入队列了
                continue;
            queue.add(new Tuple(tmp.x + 1,tmp.y,matrix[tmp.x + 1][tmp.y]));
        }
        return queue.poll().val;
    }
}
//实现Comparable接口要覆盖compareTo方法, 在compareTo方法里面实现比较
//自定义一个数据结构-元组,继承自comparable接口
class Tuple implements Comparable<Tuple>{
    int x;
    int y;
    int val;
    public Tuple(int x,int y,int val){
        this.x = x;
        this.y = y;
        this.val = val;
    }
    @Override
    public int compareTo(Tuple t){
        return this.val - t.val; //若新加入的元素较小,则排在队首
    }
}

② 二分查找。如果二分index的话,每次找到midx和midy之后,之后index很难表示出来。看了网上给的解法,没有二分index,二分的是结果。每次找到一个mid,然后求比它小的元素的个数,根据个数大于k还是小于k来二分。

class Solution { //1ms
    public int kthSmallest(int[][] matrix,int k){
        int row = matrix.length;
        int col = matrix[0].length;
        int start = matrix[0][0];
        int end = matrix[row - 1][col - 1];
        while(start < end){
            int mid = start + (end - start) / 2;
            int count = count(matrix,mid);
            if (count < k){
                start = mid + 1;
            }else{
                end = mid;
            }
        }
        return end;
    }
    public int count(int[][] matrix,int target){
        int row = matrix.length;
        int col = matrix[0].length;
        int i = row - 1;
        int j = 0;
        int count = 0;
        while(i >= 0 && j < col) {
            if (matrix[i][j] <= target) {
                count += i + 1;
                j++;
            } else {
                i--;
            }
        }
        return count;
    }
}

 

转载于:https://my.oschina.net/liyurong/blog/1596193

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值