【中等】力扣算法题解析LeetCode378:有序矩阵中第 K 小的元素

关注文末推广名片,即可免费获得本题测试源码

题目来源:LeetCode 378:有序矩阵中第 K 小的元素

问题抽象: 给定一个 n×n 整数矩阵 matrix(每行和每列均按 非递减顺序 排序)和一个整数 k1 ≤ k ≤ n²),需找到矩阵中第 k 小的元素(从小到大排序后第 k 个元素),满足以下核心需求:

  1. 矩阵特性

    • 矩阵元素在行和列方向均 非递减有序(允许重复值);
    • 左上角为最小值,右下角为最大值。
  2. 目标定义

    • k 小元素定义为 升序排序后第 k 个元素(从 1 开始计数);
    • 重复元素独立计数(如 [1,2,2,3] 中第 3 小为 2)。
  3. 优化目标

    • 避免展开矩阵排序(时间复杂度 O(n² log n) 不可接受);
    • 利用行列有序性:
      • 二分查找:在值域 [min, max]min=matrix[0][0], max=matrix[n-1][n-1])内二分猜测目标值;
      • 统计计数:对每个猜测值 mid,统计矩阵中 ≤ mid 的元素数量(需满足 ≥ k)。
  4. 边界处理

    • 单元素矩阵:直接返回该元素;
    • k=1 或 k=n²:返回左上角或右下角元素;
    • 重复值:如 k=3 时矩阵 [1,2,2,3] 返回 2
    • 统计优化
      • 从矩阵 左下角向右上角 遍历统计(或右上向左下),利用有序性在 O(n) 时间内完成。
  5. 计算约束

    • 时间复杂度 O(n log(max-min))
      • 二分迭代次数 O(log(max-min))
      • 单次统计 O(n)(行列遍历);
    • 空间复杂度 O(1):仅存储边界和计数器;
    • 输入规模:n ≤ 200(矩阵元素数 ≤ 40000)。

输入n×n 矩阵 matrix1 ≤ n ≤ 200,元素值 [-10^9, 10^9]);整数 k1 ≤ k ≤ n²)。
输出:第 k 小的元素(整数)。


解题思路

核心思想:利用矩阵的行列有序性,结合二分查找和双指针统计元素个数。
关键点

  1. 二分查找:矩阵最小值matrix[0][0]为下界,最大值matrix[n-1][n-1]为上界
  2. 统计策略:从右上角开始遍历矩阵,时间复杂度仅需 O(n)
    • 若当前元素 ≤ mid:该行左侧元素均 ≤ mid,计数增加 j+1,下移一行
    • 若当前元素 > mid:左移一列继续判断
  3. 二分调整
    • 统计数 < k:第 k 小元素在较大区间,调整左边界
    • 统计数 ≥ k:第 k 小元素在较小区间,调整右边界

优势:时间复杂度 O(n log(max-min)):优于排序解法 O(n² log n) 。空间复杂度 O(1):无需额外存储空间。


代码实现(Java版)🔥点击下载源码

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        int left = matrix[0][0];   // 矩阵最小值
        int right = matrix[n - 1][n - 1];  // 矩阵最大值

        // 二分查找:在 [left, right] 区间寻找第k小的元素
        while (left < right) {
            int mid = left + ((right - left) >> 1);  // 避免溢出的中间值计算
            int count = countLessOrEqual(matrix, mid, n);  // 统计≤mid的元素数量
            
            if (count < k) {
                left = mid + 1;  // 第k小元素在右半区间
            } else {
                right = mid;     // 第k小元素在左半区间(包含mid)
            }
        }
        return left;  // 最终left即为第k小元素
    }

    // 统计矩阵中≤mid的元素个数(从右上角开始遍历)
    private int countLessOrEqual(int[][] matrix, int mid, int n) {
        int count = 0;
        int i = 0;          // 起始行:第0行
        int j = n - 1;      // 起始列:最后一列

        while (i < n && j >= 0) {
            if (matrix[i][j] <= mid) {
                count += j + 1;  // 当前行前j+1个元素均≤mid
                i++;              // 检查下一行
            } else {
                j--;              // 当前元素过大,左移一列
            }
        }
        return count;
    }
}

代码说明

  1. 主方法 kthSmallest

    • 初始化:取矩阵最小值和最大值作为二分边界
    • 二分循环:通过统计结果调整区间,最终收敛到第k小元素
    • 边界调整
      • count < k 时说明目标值大于mid,故 left = mid + 1
      • count >= k 时说明目标值≤mid,故 right = mid
  2. 辅助方法 countLessOrEqual

    • 遍历起点:从右上角 (0, n-1) 开始
    • 核心逻辑
      • matrix[i][j] <= mid:该行前j+1个元素均符合条件,计数后下移(i++
      • matrix[i][j] > mid:当前值过大,左移一列(j--
    • 终止条件:行超出下边界或列超出左边界
  3. 复杂度分析

    • 时间:二分循环 O(log(max-min)),每次统计 O(n),综合 O(n log(max-min))
    • 空间:仅用常数变量,O(1)

提交详情(执行用时、内存消耗)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达文汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值