二分查找专题(十):“Z字形”的降维!当二分查找“失效”时
哈喽各位,我是前端小L。
欢迎来到我们的二分查找专题第十篇!我们手握“万能模板”,正准备大杀四方。面对今天这个“行有序、列有序”的矩阵,我们的第一反应可能是:
-
“拉平”?
[...5], [...6]。不行,6比5大,但[...5, 4]呢?4比5小。它无法拉平成一个完美的一维有序数组。 -
对每一行都二分查找?
for循环m行,每行 O(log n)。总时间 O(m log n)。 -
对每一列都二分查找?
for循环n列,每列 O(log m)。总时间 O(n log m)。
这两种 O(n log m) 或 O(m log n) 的解法是可行的,但... 它们足够“高质量”吗?有没有一种更巧妙的、不依赖二分查找的 O(m+n) 解法呢?
有!
力扣 240. 搜索二维矩阵 II
https://leetcode.cn/problems/search-a-2d-matrix-ii/

题目分析:
-
输入:一个
m x n矩阵。 -
属性:
-
每行从左到右升序。
-
每列从上到下升序。
-
-
目标:判断
target是否存在。
“Aha!”时刻:从“角落”出发的“Z字形”搜索
让我们站在矩阵的右上角 (row = 0, col = n-1),把它当作一个“起点”。设当前元素为 current = matrix[row][col]。 我们来和 target 比较:
-
Case 1:
current == target-
找到了!
return true。
-
-
Case 2:
current > target-
current比target大。 -
target应该在哪里?current下方的元素都比current大(列有序),所以target不可能在current下方。 -
target唯一的可能性,是在current的左侧。 -
决策:向左移动。
col--。
-
-
Case 3:
current < target-
current比target小。 -
target应该在哪里?current左侧的元素都比current小(行有序),所以target不可能在current左侧。 -
target唯一的可能性,是在current的下方。 -
决策:向下移动。
row++。
-
这个过程,就像在矩阵上走一个“Z”字形(或“7”字形)。 每一步,我们都能根据比较结果,明确地排除掉一整行或一整列,从而不断逼近 target,或者走出边界(说明不存在)。
(同理,你也可以从左下角 (row = m-1, col = 0) 开始,current > target 则 row--,current < target 则 col++。)
代码实现 (O(m+n) 巧解)
#include <vector>using namespace std;class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {if (matrix.empty() || matrix[0].empty()) {return false;}int m = matrix.size(); // 行数int n = matrix[0].size(); // 列数// 1. 从“右上角”开始int row = 0;int col = n - 1;// 2. 循环条件:只要还在矩阵内while (row < m && col >= 0) {int current = matrix[row][col];if (current == target) {return true;} else if (current > target) {// target 必在左方,排除当前列col--;} else { // current < target// target 必在下方,排除当前行row++;}}// 走出了边界,没找到return false;}
};
深度复杂度分析
-
时间复杂度 O(m + n):
-
我们的起始点是
(0, n-1),终点是(m, -1)或(-1, n)之外。 -
每一步
while循环,row指针只增不减(最多m步),col指针只减不增(最多n步)。 -
两个指针移动的总步数之和,最多是
m + n步。 -
总时间复杂度 O(m + n)。这比 O(m log n) 或 O(n log m) 都要快!
-
-
空间复杂度 O(1):
-
只使用了
row,col,m,n等常数个额外变量。
-
总结:算法的“甄别”与“适配”
今天这道题,是二分查找专题中一个绝佳的“反例”。它深刻地教会我们:
不要被“有序”二字“冲昏头脑”。并非所有“有序”问题,都必须用二分查找。
-
LC 74 (强有序):属性允许“降维”,二分查找是最优解 O(log(mn))。
-
LC 240 (弱有序):属性不允许“降维”,二分查找(O(m log n))不是最优解。反而,一个巧妙的 O(m+n) 线性搜索(Z字形)才是王道。
这体现了算法学习的更高境界:根据问题的具体约束,为它“适配”最恰当的算法。
在下一篇中,我们将回归“答案二分”的主线,去解决一个非常经典的“最小化最大值”问题。
下期见!
