力扣hot100-240.搜索二维矩阵2-单调性剪枝详解

LeetCode 240. 搜索二维矩阵 II:单调性剪枝详解

1. 算法思想

这题属于:

text 复制代码
矩阵搜索 / 单调性剪枝

也常被称为 Z 字形搜索。它不是普通二分查找:每一行、每一列分别有序,但整个矩阵按行展开后并不整体有序。

例如:

text 复制代码
[
  [1, 4, 7],
  [2, 5, 8],
  [3, 6, 9]
]

按行展开是 1, 4, 7, 2, 5, 8, 3, 6, 9,其中 7 后面是 2,因此不能把它当一维数组二分。

本题的关键是:从右上角开始,每次比较都能确定排除一整行或一整列。

2. 为什么从右上角开始

右上角 matrix[0][n - 1] 有两个相反的单调方向:

text 复制代码
左边都更小。
下面都更大。

因此当前位置 matrix[row][col]target 比较后,移动方向是确定的:

text 复制代码
current > target:向左,排除当前列。
current < target:向下,排除当前行。
current == target:找到答案。

当前值过大,为什么排除当前列

如果:

text 复制代码
current > target

当前列从上到下递增。从当前行往下的元素都满足:

text 复制代码
matrix[i][col] >= current > target

整列都太大,不可能有答案,因此:

java 复制代码
col--;

当前值过小,为什么排除当前行

如果:

text 复制代码
current < target

当前行从左到右递增。当前剩余区域中,这一行从左边界到当前列的所有元素都满足:

text 复制代码
matrix[row][j] <= current < target

整行都太小,不可能有答案,因此:

java 复制代码
row++;

3. 用 target = 5 完整模拟

text 复制代码
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]
]

从右上角开始:

text 复制代码
row = 0, col = 4,当前值 15
15 > 5,向左。

row = 0, col = 3,当前值 11
11 > 5,向左。

row = 0, col = 2,当前值 7
7 > 5,向左。

row = 0, col = 1,当前值 4
4 < 5,向下。

row = 1, col = 1,当前值 5
找到目标。

路径是:

text 复制代码
15 -> 11 -> 7 -> 4
                   |
                   v
                   5

因此这类搜索也叫 Z 字形搜索。

4. 为什么不会漏掉答案

当前位置始终是当前剩余区域的右上角。

text 复制代码
当前值大于 target:当前列下面的值更大,整列排除安全。
当前值小于 target:当前行左边的值更小,整行排除安全。

每一步删除的都是确定不可能包含 target 的区域。搜索会在找到目标,或剩余区域为空时结束,因此不会漏掉答案。

5. Java 代码完整注释

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        // 空矩阵中不存在目标。
        if (matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }

        int m = matrix.length;
        int n = matrix[0].length;

        // 从右上角开始。
        int row = 0;
        int col = n - 1;

        // row 只向下移动,col 只向左移动。
        // 任一指针越界时,说明剩余区域为空。
        while (row < m && col >= 0) {
            int current = matrix[row][col];

            if (current == target) {
                return true;
            }

            if (current > target) {
                // 当前列从上到下递增,下面的元素只会更大。
                // 所以当前列不可能有 target,向左排除它。
                col--;
            } else {
                // 当前行从左到右递增,左边的元素只会更小。
                // 所以当前行不可能有 target,向下排除它。
                row++;
            }
        }

        return false;
    }
}

6. 左下角也可以

左下角同样有相反的单调方向:上面更小,右边更大。

因此从左下角开始也可以:

text 复制代码
当前值大于 target:向上。
当前值小于 target:向右。

它与右上角写法本质相同。学习时固定记住右上角版本即可:

text 复制代码
大了向左,小了向下。

7. 复杂度分析

每一步只会向左移动一列,或者向下移动一行。

text 复制代码
最多向左 n 次。
最多向下 m 次。

所以时间复杂度是:

text 复制代码
O(m + n)

额外空间复杂度是:

text 复制代码
O(1)

8. 总结

这题属于矩阵搜索 / 单调性剪枝。

从右上角开始:

text 复制代码
当前值 > target:当前列全都太大,向左。
当前值 < target:当前行全都太小,向下。

每一步排除一整行或一整列,因此效率是 O(m + n)

一句话记忆:

text 复制代码
从右上角开始:大了向左,小了向下。
相关推荐
罗西的思考17 小时前
【Agent OS / AIOS】AOHP 深度解读:当 OS 开始为 Agent 而设计
人工智能·算法·机器学习
民乐团扒谱机18 小时前
【微实验】组合优化matlab实战(马科维茨投资模型):在收益与风险之间,寻找最优的人生配比
大数据·人工智能·算法·机器学习·数学建模·matlab·组合优化
Nil20819 小时前
leetcode 160相交链表
算法·leetcode·链表
迷途之人不知返20 小时前
算法系列2:滑动窗口
算法
Herbert_hwt20 小时前
C语言零基础入门:循环控制与数据类型详解
c语言·数据结构·算法
晚风醉蝶21 小时前
1-6-插入排序-InsertionSort
java·数据结构·排序算法
Tyler_TXZ21 小时前
C++C语言之——二叉树
c语言·开发语言·数据结构·c++·二叉树
Tisfy21 小时前
LeetCode 3090.每个字符最多出现两次的最长子字符串:二重循环 / 滑动窗口
算法·leetcode·字符串·题解·模拟·双指针·滑动窗口
-dzk-21 小时前
【技巧】LC 136.只出现一次的数字
算法·异或
有点。21 小时前
C++二叉树二(练习题)
数据结构·c++·算法·图论