题目描述
矩阵每行从左到右升序,且每行第一个数都大于上一行最后一个数。判断 target 在不在矩阵里,在返回 true,否则 false。
思路解析
挨个看每个格子当然能找到,可这把「整体有序」这个大礼包完全浪费了。题目都把有序明牌摆你面前了,必须把二分用起来。
转折:既然拉直就是升序数组,那就对下标 0..m·n−1 这条「虚拟数组」直接二分。要取第 mid 个数时,行号 = mid // n、列号 = mid % n(n 是列数)。这样根本不用真把矩阵拼起来,O(1) 就能换算回原矩阵取值。
准备 · 拉直成升序数组:把矩阵拉直就是这条数组。l=0、r=7,范围是全部 8 个数。要找 target = 3,全程不真拼数组,靠 mid//n、mid%n 回原矩阵取值。
第 1 轮 · 取 mid:中点 mid = (0+7)//2 = 3。换算回矩阵:行 = 3//4 = 0、列 = 3%4 = 3,取到 matrix[0][3] = 7。拿它和 target = 3 比。
第 1 轮 · 判断收缩:7 比 3 大,目标在左半边。把 r 收到 mid−1 = 2,右边那半(含 7,灰)整个丢掉。范围缩到 [0, 2]。
第 2 轮 · 取 mid:现在范围是 [0, 2]。新中点 mid = (0+2)//2 = 1,换算:行 = 1//4 = 0、列 = 1%4 = 1,取到 matrix[0][1] = 3。
第 2 轮 · 命中!:matrix[0][1] 正好等于 3!返回 true。全程只看了 2 个数,靠的就是 mid//n、mid%n 这一步换算。
再看个找不到的 · target=4:换个例子:找 4。数组里没有 4,看二分怎么靠 l 越过 r 收尾。
4 · mid=3 → 7 大于 4:mid=3 取到 7,比 4 大,r 收到 2。范围 [0, 2]。
4 · mid=1 → 3 小于 4:mid=1 取到 3,比 4 小,l 收到 2。现在 l 和 r 都指向 2。
4 · mid=2 → 5 大于 4,l 越过 r:mid=2 取到 5,比 4 大,r 收到 1。这时 l(2) 越过了 r(1),范围空了,循环结束,返回 false。
看到「每行升序 + 行间衔接」,就把二维拉直当一维,用 mid // n 和 mid % n 来回换算下标,剩下交给二分。这是把高维问题压成一维有序的经典技巧。
参考代码(Python)
1def searchMatrix(matrix, target):
2 m, n = len(matrix), len(matrix[0])
3 l, r = 0, m * n - 1 # 虚拟数组下标 [0, m·n−1]
4 while l <= r: # 找具体值,闭区间带等号
5 mid = (l + r) // 2
6 v = matrix[mid // n][mid % n] # 一维下标还原行列,n 是列数
7 if v == target: return True
8 elif v < target: l = mid + 1 # 偏小,丢左半
9 else: r = mid - 1 # 偏大,丢右半
10 return False复杂度分析
- 时间复杂度:O(log(m·n)) —— 在 m·n 个数上二分,每轮砍一半
- 空间复杂度:O(1) —— 只用 l、r、mid 几个变量,不真拼数组
套路模板
记牢换算公式:行 = mid // n、列 = mid % n,n 是列数。这是全题唯一的坎,背下来普通二分就能解二维。
1l, r = 0, m * n - 1
2while l <= r:
3 mid = (l + r) // 2
4 v = matrix[mid // n][mid % n] # 行 = mid//n,列 = mid%n
5 if v == target: return True
6 elif v < target: l = mid + 1
7 else: r = mid - 1
8return False易错点
- 错误写法:mid // m、mid % m(拿行数 m 换算) → 正确写法:mid // n、mid % n(拿列数 n 换算)(拉直是按行铺的,每行有 n 个数,所以除、模都用列数 n;用行数 m 会算出完全错的格子)
- 错误写法:r = m * n(越界) → 正确写法:r = m * n − 1(一共 m×n 个数,最大下标是 m×n−1,写成 m·n 会越界访问)
- 错误写法:while l < r(漏等号) → 正确写法:while l <= r(这题找的是具体值、不是边界,区间缩到只剩一个数时还得比一次,漏等号会漏判最后一个数)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。