中等
给你一个满足下述两条属性的 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
这是为您定制的 搜索二维矩阵 (二分查找 / 虚拟扁平化 / 开区间) 复习笔记。
这份代码使用了 "虚拟扁平化" 的技巧,配合您习惯的 "开区间 (left, right)****" 写法。这种写法的优势在于不用纠结边界 +1 还是 -1,逻辑非常干净。
📝 核心笔记:搜索二维矩阵 (Search a 2D Matrix)
1. 核心思想 (一句话总结)
"降维打击:把它当成一个长长的有序数组。"
虽然输入是二维的,但因为题目保证了"每行有序"且"下一行比上一行大",所以逻辑上它就是一个长度为 m \\times n 的一维有序数组。
我们只需要搞定 一维下标 (mid) 到 二维坐标 (row, col) 的数学映射即可。
💡 坐标映射公式:
设列数为 n:
- 行号 (Row) =
mid / n(商:表示跨越了几整行) - 列号 (Col) =
mid % n(余:表示在当前行的偏移量)
2. 算法流程 (开区间版)
- 定义区间:
-
- 范围是
0到m*n - 1。 - 开区间 初始化:
left = -1,right = m * n。
- 范围是
- 二分查找:
-
- 取出
mid。 - 映射 :
x = matrix[mid / n][mid % n]。 - 比较:
- 取出
-
-
x == target: 找到了,return true。x < target: 太小了,left = mid(往右找)。x > target: 太大了,right = mid(往左找)。
-
- 终止:
-
- 当
left + 1 == right时循环结束。如果中间没返回true,说明不存在,return false。
- 当
🔍 代码回忆清单 (带注释版)
📝 核心笔记:搜索二维矩阵 (Z-Search / Staircase)
1. 核心思想 (一句话总结)
"站在右上角,把矩阵当成一颗二叉搜索树 (BST)。"
为什么选右上角?因为在这个角:
- 向左 看:数字变小。
- 向下看:数字变大。
方向是确定的!(如果选左上角,向右向下都变大,就无法决策了)。
💡 图像记忆 (抽象 BST):
- 把右上角的元素想象成 Root 节点。
- 它的左边是左子树(比它小)。
- 它的下边是右子树(比它大)。
- 你要找 Target,就像在 BST 里遍历一样,大了往左走,小了往下走。
2. 算法流程 (三步走)
- 定位起点 :初始化指针在
(0, col-1),即右上角。 - 循环决策:
-
- 相等 :
return true。 - 当前值 > Target :太大啦!说明这一列剩下的数(都在下面)肯定更大,排除这一列 (
j--,向左)。 - 当前值 < Target :太小啦!说明这一行之前的数(都在左边)肯定更小,排除这一行 (
i++,向下)。
- 相等 :
- 越界终止:如果走出了矩阵范围还没找到,说明不存在。
🔍 代码回忆清单 (带注释版)
// 题目:LC 240. Search a 2D Matrix II (也适用于 LC 74)
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
// 1. 起点:右上角
int i = 0;
int j = matrix[0].length - 1;
// 2. 边界条件:只要在矩阵范围内就继续搜
while (i < matrix.length && j >= 0) {
if (matrix[i][j] == target) {
return true; // 🎯 找到了
}
// 3. 核心逻辑:消消乐
if (matrix[i][j] < target) {
// 当前值比目标小,说明这一行左边的更小,没戏了
// 需要找更大的数,所以往下走
i++;
} else { // matrix[i][j] > target
// 当前值比目标大,说明这一列下边的更大,没戏了
// 需要找更小的数,所以往左走
j--;
}
}
return false; // 走出边界都没找到
}
}
⚡ 快速复习 CheckList (易错点)
-
\] **起点只能是右上角吗?**
-
- 右上角 (Right-Top) 或 左下角 (Left-Bottom) 都可以。这两个角都有"一边大一边小"的性质。
- 左上角 和 右下角 绝对不行!(因为两个方向都是同向变化的,没法做排除法)。
-
\] **时间复杂度是多少?**
-
- O(m + n)。
- 最坏情况就是沿着对角线或者边缘走一趟"L"型,最多走 m 行 n 列。
- 注:对于 LC 74 (全有序),二分法是 O(\\log(mn)) 更快;但对于 LC 240 (局部有序),这个算法是 O(m+n) 最优。
-
\] **边界检查** **j >= 0****?**
-
- 别忘了检查
j不能减到 -1。同理i不能增加到m。
- 别忘了检查
🖼️ 数字演练
矩阵:
1 4 7
2 5 8
3 6 9
Target: 5
- Start (0, 2) : 值是
7。
-
7 > 5(太大了)。- Action :
j--(往左)。排除第 2 列。
- Point (0, 1) : 值是
4。
-
4 < 5(太小了)。- Action :
i++(往下)。排除第 0 行。
- Point (1, 1) : 值是
5。
-
5 == 5。- Return True.
(路径:7 -> 4 -> 5)