题目来源:LeetCode 378:有序矩阵中第 K 小的元素
问题抽象: 给定一个 n×n 整数矩阵 matrix(每行和每列均按 非递减顺序 排序)和一个整数 k(1 ≤ k ≤ n²),需找到矩阵中第 k 小的元素(从小到大排序后第 k 个元素),满足以下核心需求:
-
矩阵特性:
- 矩阵元素在行和列方向均 非递减有序(允许重复值);
- 左上角为最小值,右下角为最大值。
-
目标定义:
- 第
k小元素定义为 升序排序后第k个元素(从1开始计数); - 重复元素独立计数(如
[1,2,2,3]中第3小为2)。
- 第
-
优化目标:
- 避免展开矩阵排序(时间复杂度
O(n² log n)不可接受); - 利用行列有序性:
- 二分查找:在值域
[min, max](min=matrix[0][0],max=matrix[n-1][n-1])内二分猜测目标值; - 统计计数:对每个猜测值
mid,统计矩阵中≤ mid的元素数量(需满足≥ k)。
- 二分查找:在值域
- 避免展开矩阵排序(时间复杂度
-
边界处理:
- 单元素矩阵:直接返回该元素;
- k=1 或 k=n²:返回左上角或右下角元素;
- 重复值:如
k=3时矩阵[1,2,2,3]返回2; - 统计优化:
- 从矩阵 左下角向右上角 遍历统计(或右上向左下),利用有序性在
O(n)时间内完成。
- 从矩阵 左下角向右上角 遍历统计(或右上向左下),利用有序性在
-
计算约束:
- 时间复杂度 O(n log(max-min)):
- 二分迭代次数
O(log(max-min)); - 单次统计
O(n)(行列遍历);
- 二分迭代次数
- 空间复杂度 O(1):仅存储边界和计数器;
- 输入规模:
n ≤ 200(矩阵元素数≤ 40000)。
- 时间复杂度 O(n log(max-min)):
输入:n×n 矩阵 matrix(1 ≤ n ≤ 200,元素值 [-10^9, 10^9]);整数 k(1 ≤ k ≤ n²)。
输出:第 k 小的元素(整数)。
解题思路
核心思想:利用矩阵的行列有序性,结合二分查找和双指针统计元素个数。
关键点:
- 二分查找:矩阵最小值
matrix[0][0]为下界,最大值matrix[n-1][n-1]为上界 - 统计策略:从右上角开始遍历矩阵,时间复杂度仅需 O(n)
- 若当前元素 ≤ mid:该行左侧元素均 ≤ mid,计数增加
j+1,下移一行 - 若当前元素 > mid:左移一列继续判断
- 若当前元素 ≤ mid:该行左侧元素均 ≤ mid,计数增加
- 二分调整:
- 统计数 < 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;
}
}
代码说明
-
主方法
kthSmallest- 初始化:取矩阵最小值和最大值作为二分边界
- 二分循环:通过统计结果调整区间,最终收敛到第k小元素
- 边界调整:
count < k时说明目标值大于mid,故left = mid + 1count >= k时说明目标值≤mid,故right = mid
-
辅助方法
countLessOrEqual- 遍历起点:从右上角
(0, n-1)开始 - 核心逻辑:
- 当
matrix[i][j] <= mid:该行前j+1个元素均符合条件,计数后下移(i++) - 当
matrix[i][j] > mid:当前值过大,左移一列(j--)
- 当
- 终止条件:行超出下边界或列超出左边界
- 遍历起点:从右上角
-
复杂度分析
- 时间:二分循环 O(log(max-min)),每次统计 O(n),综合 O(n log(max-min))
- 空间:仅用常数变量,O(1)
提交详情(执行用时、内存消耗)

906

被折叠的 条评论
为什么被折叠?



