378. 有序矩阵中第 K 小的元素

目录

方法一:直接排序

思路及算法

复杂度分析

方法二:二分查找

复杂度分析

同类型的题:

668.乘法表中第k小的数

875.爱吃香蕉的珂珂

复杂度分析


378. 有序矩阵中第 K 小的元素

378. 有序矩阵中第 K 小的元素_第1张图片

方法一:直接排序

思路及算法

最直接的做法是将这个二维数组转成一维数组,并对该一维数组进行排序。最后这个一维数组中的第 k 个数即为答案。

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int rows = matrix.length, columns = matrix[0].length;
        int[] sorted = new int[rows * columns];
        int index = 0;
        for (int[] row : matrix) {
            for (int num : row) {
                sorted[index++] = num;
            }
        }
        Arrays.sort(sorted);
        return sorted[k - 1];
    }
}

复杂度分析

时间复杂度:O( m*n log m*n)——>2O( m*n log n)——>O( n*n log n) (假设m和n数值相当)

空间复杂度:O(m*n)  一维数组存储这m*n个数字

方法二:二分查找

思路及算法

由题目给出的性质可知,这个矩阵内的元素是从左上到右下递增的。我们知道整个二维数组中 matrix[0][0]为最小值matrix[m - 1][n - 1]为最大值,现在我们将其分别记作 l 和 r。

可以发现:任取一个数 mid满足 l ≤ mid ≤ r,那么矩阵中不大于 mid 的数,肯定全部分布在矩阵的左上角。假设mid = 8:

378. 有序矩阵中第 K 小的元素_第2张图片

 我们需要:

1. 计算每次 mid 分界线上半部分的元素个数count:

①通用方法计算:从最后一行第一列进行判断,如果该元素小于mid,count就加上这一列,列数加一,否则行数减一。直至遍历完毕(具体看下面代码)

2. 然后比较 count 和 k:

①如果count >= k,说明 right 依旧可以前移,由于存在重复的元素,因此right = mid;

②否则说明少于k个,最终的mid在 此次mid的右边,因此需要right右移,即right = mid+1

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int m = matrix.length, n = matrix[0].length;
        int left = matrix[0][0], right = matrix[m-1][n-1];
        while(left < right) {
            int mid = left + (right -left) / 2;
            //计算count
            int i = m - 1 , j = 0;
            int count = 0;
            while(i >= 0 && j < m) {
                if(matrix[i][j] <= mid) {
                    count += i + 1;  //i是下标从0开始,所以+1
                    j++;
                }else{
                    i--;
                }
            }
            //二分定位
            if(count >= k){
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        return left;
    }
}

注意:

① count >= k, 在count > k时,为什么不取 right = mid-1,而是right = mid。因为我们的目标值可能存在重复,比如 123334,如果我选择要找第3小的数,而mid当前恰好=3,那么count得到的结果会是5(<=mid)。如果我们选择right = mid-1 = 2。那么将会运行错误导致结果错误。

② 在temp = k时,为什么不能立马返回结果呢,而是继续运行缩小边界?因为我们当前的mid可能是一个不在乘法表中的值,毕竟mid= (left+right)  >> 1;  所以立即返回可能返回错误答案。所以一定收缩范围 直到left=right。

③ 最终返回的一定是正确值(若答案 t 的 count = k, 而某一非表值 x 的 count也=k, 那么t一定比x小,最终x也会被right缩小导致出局)。

复杂度分析

时间复杂度:O( m*n log m*n)——>2O( m*n log n)——>O( n*n log n) (假设m和n数值相当)

空间复杂度:O(1) 

同类型的题:

668.乘法表中第k小的数

668. 乘法表中第k小的数

378. 有序矩阵中第 K 小的元素_第3张图片

 整体思路一致,只是count的计算可以优化

class Solution {
    public int findKthNumber(int m, int n, int k) {
        int left = 1, right = m*n;
        while(left < right) {
            int mid = left + (right-left) / 2;
            //计算count
            int count = (mid / n) *n;
            for(int i = mid / n + 1; i <= m; i++) {
                count += mid / i;
            }
            //二分定位
            if(count < k){
                left = mid + 1;
              
            }else {
                right = mid;
            }
        }
        return left;
    }
}

复杂度分析

时间复杂度:O( m*n log m*n),mn数值相当时,O(n^2logn)。

空间复杂度:O(1) 

875.爱吃香蕉的珂珂

875. 爱吃香蕉的珂珂

378. 有序矩阵中第 K 小的元素_第4张图片

向上取整的写法值的学习:(pile - 1) / mid + 1;

class Solution {
    public int minEatingSpeed(int[] piles, int h) {
        //left需要设置为1,最少一小时吃一根香蕉
        int left = 1, right = 1;
        for (int pile : piles) {
            right = Math.max(right, pile);
        }
        while(left < right) {
            int mid = left + (right - left) / 2;
            //计算count
            int count = 0;
            for(int pile : piles) {
                count += (pile - 1) / mid + 1;  //向上取整
                //下面也可以实现向上取整,但是不够优化
                // if(pile % mid != 0){
                //     count += pile / mid + 1;
                    
                // }else{
                //     count += pile / mid;
                // }
            }
            //二分定位
            if(count > h){
                left = mid + 1;
            }else{
                right = mid;
            }
        }
        return left;
    }
}

复杂度分析

时间复杂度:O( n log m),其中m为piles数组最大值,n主要是用于计算每一轮count。

空间复杂度:O(1) 

你可能感兴趣的:(数据结构,LeetCode,矩阵,线性代数,数据结构)