LeetCode Top Interview Questions 378. Kth Smallest Element in a Sorted Matrix (Java版; Meidum)

welcome to my blog

LeetCode Top Interview Questions 378. Kth Smallest Element in a Sorted Matrix (Java版; Meidum)

题目描述
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
//自定义节点类
class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length, m = matrix[0].length;
        List<PriorityQueue<Integer>> list = new ArrayList<>();
        PriorityQueue<Node> heap = new PriorityQueue<>(new Comparator<Node>(){
            public int compare(Node n1, Node n2){
                return matrix[n1.x][n1.y] - matrix[n2.x][n2.y];
            }
        });

        for(int i=0; i<n; i++){
            heap.add(new Node(i, 0));
        }
        int res=0;
        for(int i=0; i<k; i++){
            Node cur = heap.poll();
            res = matrix[cur.x][cur.y];
            //注意检查纵坐标索引是否越界
            if(cur.y+1<m){
                cur.y = cur.y+1;
                heap.add(cur);
            }
            
        }
        return res;
    }

    class Node{
        int x;
        int y;
        Node(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
}
class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length, m = matrix[0].length;
        int left=matrix[0][0], right=matrix[n-1][m-1], mid;
        while(left < right){
            mid = left + (right - left)/2;
            int[] smallbig = {Integer.MIN_VALUE, Integer.MAX_VALUE};
            int count = cal(matrix, mid, smallbig);
            if(count==k){
                return smallbig[0];
            }else if(count > k){
                right = smallbig[0];
            }else{
                left = smallbig[1];
            }
        }
        return left;
    }
    //计算matrix中有多少个数小于等于mid
    private int cal(int[][] matrix, int mid, int[] smallbig){
        int n = matrix.length, m = matrix[0].length;
        int i = n-1, j = 0;
        int count = 0;
        while(i>=0 && j<m){
            if(matrix[i][j] <= mid){
                count += i+1;
                smallbig[0] = Math.max(smallbig[0], matrix[i][j]);
                j++;
            }else{
                smallbig[1] = Math.min(smallbig[1], matrix[i][j]);
                i--;
            }
        }
        return count;
    }
}
第一次做; 二分法; 核心:1)对number range进行二分 2)记录小于mid的最大值和大于mid的最小值

An alternate could be to apply the Binary Search on the “number range” instead of the “index range”

//对number range进行二分
//对number range进行二分
class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        int lo = matrix[0][0], hi = matrix[n-1][n-1];
        while(lo < hi){
            int mid = lo + ((hi-lo)>>1);
            int[] smallbig = {Integer.MIN_VALUE, Integer.MAX_VALUE};
            int count = handle(matrix, mid, smallbig);
            if(count==k)
                return smallbig[0];
            else if(count<k)
                lo = smallbig[1];
            else
                hi = smallbig[0];
        }
        //能执行到这里,比如案例[[-5]], 所以直接返回lo或者hi就可以了
        return hi;
    }
    private int handle(int[][] matrix, int mid, int[] smallbig){
        int count = 0, n = matrix.length;
        //从矩阵的左下角开始移动, 根据矩阵的单调性, 要么往上移动, 要么往右移动
        int i=n-1, j=0;
        //统计矩阵中小于等于mid的元素数量
        while(i>=0 && j<matrix[0].length){
            if(matrix[i][j] <= mid){
                //竖直方向上, (0,j)到(i,j)都小于等于mid
                count += i+1;
                smallbig[0] = Math.max(smallbig[0], matrix[i][j]);
                j++;
            }
            else{
                //水平方向上, (i,j)到(i,n-1)都大于mid
                smallbig[1] = Math.min(smallbig[1], matrix[i][j]);
                i--;
            }
        }
        return count;
    }
}
第一次做; 维护一个大小为k的最小堆; 矩阵每一行都是有序的; 本题最核心: cur不是第k个节点的话, 就把cur所在行的下一个节点加入最小堆
class Solution {
    //内部类; 定义节点
    class Node{
        int x, y;
        Node(int x, int y){
            this.x=x;
            this.y=y;
        }
    }
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        //java新特性; 使用lambda表达式写比较器; 按照值的大小升序排序
        Queue<Node> minHeap = new PriorityQueue<Node>((a,b)->matrix[a.x][a.y] - matrix[b.x][b.y]);
        //先把矩阵每一行的第一个元素添加至最小堆
        for(int i=0; i<n; i++){
            minHeap.add(new Node(i,0));
        }
        //找到第k个元素
        int count = 0;
        while(true){
            Node cur = minHeap.poll();
            int val = matrix[cur.x][cur.y];
            count++;
            if(count==k)
                return val;
            //本题最核心: cur不是第k个节点的话, 就把cur所在行的下一个节点加入最小堆
            //注意检查索引是否越界
            if(cur.y+1<n){
                cur.y++;
                minHeap.add(cur);
            }
        }  
    }
}
LeetCode优秀题解 最小堆
import java.util.*;

class Node {
  int row;
  int col;

  Node(int row, int col) {
    this.row = row;
    this.col = col;
  }
}

class Solution {

  public static int findKthSmallest(int[][] matrix, int k) {
    PriorityQueue<Node> minHeap = new PriorityQueue<Node>((n1, n2) -> matrix[n1.row][n1.col] - matrix[n2.row][n2.col]);

    // put the 1st element of each row in the min heap
    // we don't need to push more than 'k' elements in the heap
    for (int i = 0; i < matrix.length && i < k; i++)
		minHeap.add(new Node(i, 0));

    // take the smallest (top) element form the min heap, if the running count is equal to k return the number
    // if the row of the top element has more elements, add the next element to the heap
    int numberCount = 0, result = 0;
    while (!minHeap.isEmpty()) {
      Node node = minHeap.poll();
      result = matrix[node.row][node.col];
      if (++numberCount == k)
        break;
      node.col++;
      if (matrix[0].length > node.col)
        minHeap.add(node);
    }
    return result;
  }
}
LeetCode优秀题解 二分法; countLessEqual()方法的核心: 以矩阵的左下角为起点, 每次要么往上移动, 要么往右移动, 这样最多移动n+n次

讲解的太棒了, 复习时看看原链接

An alternate could be to apply the Binary Search on the “number range” instead of the “index range”. As we know that the smallest number of our matrix is at the top left corner and the biggest number is at the bottom lower corner. These two number can represent the “range” i.e., the start and the end for the Binary Search. Here is how our algorithm will work:

Start the Binary Search with start = matrix[0][0] and end = matrix[n-1][n-1].
Find middle of the start and the end. This middle number is NOT necessarily an element in the matrix.
Count all the numbers smaller than or equal to middle in the matrix. As the matrix is sorted, we can do this in O(N).
While counting, we can keep track of the “smallest number greater than the middle” (let’s call it n1) and at the same time the “biggest number less than or equal to the middle” (let’s call it n2). These two numbers will be used to adjust the "number range" for the Binary Search in the next iteration.
If the count is equal to ‘K’, n1 will be our required number as it is the “biggest number less than or equal to the middle”, and is definitely present in the matrix.
If the count is less than ‘K’, we can update start = n2 to search in the higher part of the matrix and if the count is greater than ‘K’, we can update end = n1 to search in the lower part of the matrix in the next iteration.
class Solution {
  public static int findKthSmallest(int[][] matrix, int k) {
    int n = matrix.length;
    int start = matrix[0][0], end = matrix[n - 1][n - 1];
    while (start < end) {
      int mid = start + (end - start) / 2;
      // first number is the smallest and the second number is the largest
      int[] smallLargePair = { matrix[0][0], matrix[n - 1][n - 1] };

      int count = countLessEqual(matrix, mid, smallLargePair);

      if (count == k)
        return smallLargePair[0];

      if (count < k)
        start = smallLargePair[1]; // search higher
      else
        end = smallLargePair[0]; // search lower
    }

    return start;
  }

  private static int countLessEqual(int[][] matrix, int mid, int[] smallLargePair) {
    int count = 0;
    //核心: 以矩阵的左下角为起点, 每次要么往上移动, 要么往右移动
    int n = matrix.length, row = n - 1, col = 0;
    while (row >= 0 && col < n) {
      if (matrix[row][col] > mid) {
        // as matrix[row][col] is bigger than the mid, let's keep track of the
        // smallest number greater than the mid
        smallLargePair[1] = Math.min(smallLargePair[1], matrix[row][col]);
        row--;
      } else {
        // as matrix[row][col] is less than or equal to the mid, let's keep track of the
        // biggest number less than or equal to the mid
        smallLargePair[0] = Math.max(smallLargePair[0], matrix[row][col]);
        count += row + 1;
        col++;
      }
    }
    return count;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值