给你一个满足下述两条属性的 m x n 整数矩阵:
- 每行中的整数从左到右按非严格递增顺序排列。
- 每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
示例 1:
输入: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出: true
示例 2:
输入: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出: false
提示:
m == matrix.lengthn == matrix[i].length1 <= m, n <= 100-104 <= matrix[i][j], target <= 104
1. 生活案例:在电影院找座位
想象你在一个电影院里:
-
布局:电影院有 排,每排有 个座位。
-
规律:票号是连续的。第一排是 1
7 号,第二排是 1020 号……每一排都比前一排的号大。 -
现状:你手里有一个票号(
target),你想知道这个票号在不在这个影院里。 -
笨办法:一排一排地找。
-
聪明办法:因为票号是整体递增的,你可以把整个电影院看成一条长长的直线队伍。
- 你先算出一共有多少个座位()。
- 用二分法在这一条长线上找。
- 当你确定是“第几个”座位时,通过简单的除法和取余,就能算出它在第几排、第几列。
2. 代码实现与详细注释
这是你图片中的代码,我为你加上了“坐标转换”逻辑的详细中文注释:
JavaScript
/**
* @param {number[][]} matrix - 二维矩阵(影院布局)
* @param {number} target - 目标值(你的票号)
* @return {boolean}
*/
var searchMatrix = function(matrix, target) {
let m = matrix.length; // 行数
let n = matrix[0].length; // 每行有多少个座位(列数)
let left = 0;
let right = m * n - 1; // 把二维拉平,最后一个座位的索引
while (left <= right) {
// 1. 找到中间那个座位的“长线索引”
let mid = Math.floor(left + (right - left) / 2);
// 2. 【核心坐标转换】:
// row = index / 每行人数 (取整)
// col = index % 每行人数 (取余)
let row = Math.floor(mid / n);
let col = mid % n;
let value = matrix[row][col]; // 找到电影院里对应的真实座位号
if (value === target) {
return true; // 找到了!
} else if (value < target) {
// 中间的号小了,往后找
left = mid + 1;
} else {
// 中间的号大了,往前找
right = mid - 1;
}
}
return false; // 找遍了,没这个票号
};
3. 核心原理解析
为什么可以这样做?
题目给出了两个关键条件:
-
每行从左到右递增。
-
每行的第一个数大于前一行的最后一个数。
这两个条件加在一起,意味着这个二维矩阵本质上就是一个排好序的一维数组,只是为了方便存储被“折断”成了几行。
坐标转换公式
如果一个二维矩阵有 列,那么一维索引 对应的二维位置为:
- 行号:
- 列号:
这个公式是处理矩阵类算法题的“金钥匙”。
复杂度分析
- 时间复杂度:。这比先找行再找列的两次二分更加直接高效。
- 空间复杂度:。只用了几个定义位置的变量。