74. 搜索二维矩阵

中等

给你一个满足下述两条属性的 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.length
  • n == matrix[i].length
  • 1 <= 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. 算法流程 (开区间版)
  1. 定义区间
    • 范围是 0m*n - 1
    • 开区间 初始化:left = -1, right = m * n
  1. 二分查找
    • 取出 mid
    • 映射x = matrix[mid / n][mid % n]
    • 比较
      • x == target: 找到了,return true
      • x < target: 太小了,left = mid (往右找)。
      • x > target: 太大了,right = mid (往左找)。
  1. 终止
    • left + 1 == right 时循环结束。如果中间没返回 true,说明不存在,return false
🔍 代码回忆清单 (带注释版)
1. 核心思想 (一句话总结)

"站在右上角,把矩阵当成一颗二叉搜索树 (BST)。"

为什么选右上角?因为在这个角:

  • 向左 看:数字变
  • 向下看:数字变大。

方向是确定的!(如果选左上角,向右向下都变大,就无法决策了)。

💡 图像记忆 (抽象 BST):

  • 把右上角的元素想象成 Root 节点
  • 它的左边是左子树(比它小)。
  • 它的下边是右子树(比它大)。
  • 你要找 Target,就像在 BST 里遍历一样,大了往左走,小了往下走。
2. 算法流程 (三步走)
  1. 定位起点 :初始化指针在 (0, col-1),即右上角。
  2. 循环决策
    • 相等return true
    • 当前值 > Target :太大啦!说明这一列剩下的数(都在下面)肯定更大,排除这一列 (j--,向左)。
    • 当前值 < Target :太小啦!说明这一行之前的数(都在左边)肯定更小,排除这一行 (i++,向下)。
  1. 越界终止:如果走出了矩阵范围还没找到,说明不存在。
🔍 代码回忆清单 (带注释版)
复制代码
// 题目: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"型,最多走 mn 列。
    • 注:对于 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

  1. Start (0, 2) : 值是 7
    • 7 > 5 (太大了)。
    • Action : j-- (往左)。排除第 2 列。
  1. Point (0, 1) : 值是 4
    • 4 < 5 (太小了)。
    • Action : i++ (往下)。排除第 0 行。
  1. Point (1, 1) : 值是 5
    • 5 == 5
    • Return True.

(路径:7 -> 4 -> 5)

相关推荐
@atweiwei1 小时前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
JYeontu1 小时前
程序员都该掌握的“质因数分解”
前端·javascript·算法
上海云盾-高防顾问1 小时前
DNS异常怎么办?快速排查+解决指南
开发语言·php
开发者小天2 小时前
python安装 Matplotlib 库 安装 Seaborn 库
开发语言·python·matplotlib
wjs20242 小时前
《Foundation 折叠列表:设计与应用解析》
开发语言
with-the-flow2 小时前
从数学底层的底层原理来讲 random 的函数是怎么实现的
c语言·python·算法
tyb3333332 小时前
leetcode:吃苹果和队列
算法·leetcode·职场和发展
多恩Stone2 小时前
【3D-AICG 系列-15】Trellis 2 的 O-voxel Shape: Flexible Dual Grid 代码与论文对应
人工智能·python·算法·3d·aigc
weixin_448119942 小时前
Datawhale 大模型算法全栈基础篇 202602第4次笔记
笔记·算法