【LeetCode Hot 100 刷题日记(21/100)】240. 搜索二维矩阵 II —— Z 字形查找、数组、二分查找、矩阵、双指针📌

64 阅读6分钟

📌 题目链接:240. 搜索二维矩阵 II - 力扣(LeetCode)

🔍 难度:中等 | 🏷️ 标签:数组、二分查找、矩阵、双指针

⏱️ 目标时间复杂度:O(m + n)

💾 空间复杂度:O(1)

🎯 核心算法:Z 字形搜索(从右上角开始的双指针遍历)

💡 面试重点:如何利用矩阵有序特性,避免暴力遍历?

🔥 考点深度:数据结构特性分析、空间优化、边界处理、逻辑推理能力


🧩 题目分析

我们面对的是一个 m × n 的二维矩阵,其具有以下两个关键性质:

每行从左到右升序排列
每列从上到下升序排列

这说明矩阵在 行方向和列方向都具备单调性,但整体并不是完全排序的(例如对角线可能不满足顺序),因此不能直接用一维二分。

🎯 目标:判断 target 是否存在于该矩阵中。

📌 典型输入示例

matrix = [
    [1,  4,  7, 11, 15],
    [2,  5,  8, 12, 19],
    [3,  6,  9, 16, 22],
    [10, 13, 14, 17, 24],
    [18, 21, 23, 26, 30]
], target = 5true

🔍 难点解析

  • 如果使用暴力法 O(mn),虽然能过,但不是最优解。
  • 使用逐行二分查找 O(m log n),是合理优化。
  • 但最巧妙的解法是利用 “右上角”或“左下角”的特殊性,实现 O(m+n) 的线性扫描!

🛠️ 核心算法及代码讲解

✅ Z 字形查找(从右上角出发)

🔍 思想本质:

🌀 利用“右上角元素是当前子矩阵的最大值(横向)且最小值(纵向)”这一特性,构建一个类似“Z”字路径的搜索策略。

我们从 右上角 (0, n-1) 开始,每次根据当前值与 target 的大小关系决定移动方向:

当前值 matrix[x][y]操作
== target找到!返回 true
> target向左移(y--)→ 排除整列(因为该列所有值都更大)
< target向下移(x++)→ 排除整行(因为该行所有值都更小)

👉 这样可以确保每一步都能排除掉至少一行或一列的数据,最终覆盖整个矩阵。

🧠 为什么选择右上角?

  • 右上角是 本行最大值,也是 本列最小值
  • 因此它是一个“决策点”,既能判断是否需要向下(比它大),也能判断是否需要向左(比它小)
  • 左下角也可以(逻辑相反),但习惯上用右上角

✅ 算法流程图(文字版):

起始: (0, n-1)
while x < m and y >= 0:
    if matrix[x][y] == target → return true
    if matrix[x][y] > target → y--
    else → x++
return false

🧭 解题思路(分步详解)

  1. 初始化起点x = 0, y = n - 1(右上角)
  2. 循环条件:只要 x < my >= 0,就继续搜索
  3. 比较当前值
    • 若等于 target:立即返回 true
    • 若大于 target:说明当前列的所有值都大于 target(因为列递增),所以 向左跳过整列
    • 若小于 target:说明当前行的所有值都小于 target(因为行递增),所以 向下跳过整行
  4. 超出边界:若 x >= my < 0,说明未找到,返回 false

🌟 关键洞察:每一次移动都是“排除不可能区域”,而不是盲目试探!


📊 算法分析

指标分析
时间复杂度O(m + n) ✅
最坏情况下走完一条对角线,最多走 m+n 步
空间复杂度O(1) ✅
仅用常数额外空间
适用场景适用于有行列单调性的二维矩阵搜索问题
面试加分项能否想到“右上角”这个切入点?这是考察思维灵活性的重要标志

⚠️ 对比其他方法:

  • 暴力遍历:O(mn) ❌ 不推荐
  • 逐行二分:O(m log n) ✅ 可接受
  • Z 字形查找:O(m+n) ✅ 最优解,体现对数据结构的理解深度

💻 代码(保留原模板,完整可运行)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();
        int x = 0, y = n - 1;  // 从右上角开始
        while (x < m && y >= 0) {
            if (matrix[x][y] == target) {
                return true;  // 找到目标值
            }
            if (matrix[x][y] > target) {
                --y;  // 当前列全部大于 target,向左移动
            } else {
                ++x;  // 当前行全部小于 target,向下移动
            }
        }
        return false;  // 遍历完未找到
    }
};

// 测试
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    // 示例1
    vector<vector<int>> matrix1 = {
        {1,  4,  7, 11, 15},
        {2,  5,  8, 12, 19},
        {3,  6,  9, 16, 22},
        {10, 13, 14, 17, 24},
        {18, 21, 23, 26, 30}
    };
    int target1 = 5;
    Solution sol;
    cout << "Test 1: " << (sol.searchMatrix(matrix1, target1) ? "true" : "false") << endl;  // true

    // 示例2
    int target2 = 20;
    cout << "Test 2: " << (sol.searchMatrix(matrix1, target2) ? "true" : "false") << endl;  // false

    return 0;
}

🎯 面试拓展与常见提问

❓ Q1:为什么不能从左上角开始?

❌ 左上角是最小值,无法判断应往右还是往下。
✅ 例如:target=5,左上角是 1,比它小 → 你不知道应该往右(可能更大)还是往下(也可能更大)。
✅ 但右上角是 15,比 5 大 → 可以安全地向左排除一列。

❓ Q2:能否用二分查找解决?

✅ 可以,但不是最优!
每行做一次二分查找:O(m log n)
而 Z 字形是 O(m+n),当 m,n 很大时优势明显。

❓ Q3:有没有递归写法?

✅ 有,但会增加栈空间开销,不推荐。
原地迭代更高效、更清晰。

❓ Q4:如果矩阵没有行列有序呢?

❌ 那只能暴力或哈希表了。
✅ 本题的解法依赖于“行列单调性”这一前提。


📚 知识延伸:同类题目对比

题号题名特性解法
74搜索二维矩阵行列有序,矩形连续二分或 Z 字形
240搜索二维矩阵 II行列有序,非连续Z 字形(最优)
378有序矩阵中第 K 小的元素行列有序二分答案 + 计数
230二叉搜索树中第 K 小的元素类似单调结构中序遍历

🔁 规律总结:遇到“有序二维结构”,优先考虑 Z 字形、对角线、二分答案 等技巧!


🌟 本期完结,下期见!🔥

👉 点赞收藏加关注,新文更新不迷路。关注专栏【算法】LeetCode Hot100刷题日记,持续为你拆解每一道热题的底层逻辑与面试技巧!

💬 欢迎留言交流你的解法或疑问!一起进步,冲向 Offer!💪


📣 下一期预告:LeetCode 热题 100 第22题 —— 25.相交链表(简单)

🔹 题目:给你两个单链表的头节点 headAheadB,请你找出并返回两个链表相交的起始节点。如果两个链表没有交点,返回 null

🔹 核心思路:双指针“同步走”技巧——当一个指针走到末尾时,跳到另一个链表头,最终在交点相遇(或同时为 null)。

🔹 考点:链表遍历、双指针、环形思想、数学对齐。

🔹 难度:简单,但思维巧妙,是面试高频题!

💡 关键洞察

  • 若两链表相交,则从交点开始,后续节点完全相同;
  • 利用路径长度对齐(A + B = B + A),让两个指针在第二轮遍历时自然对齐。

⚠️ 注意:不能修改链表结构,也不能使用哈希表(虽然可行,但空间复杂度 O(n),非最优)!


📌 记住:当你在刷题时,不要只看答案,要像写这篇文章一样,深入思考每一步背后的原理、优化空间和面试价值。这才是真正提升算法能力的方式!


🎉 感谢阅读! 你的每一次点赞、收藏、转发,都是我坚持输出高质量内容的动力!🚀