【中等】74. 搜索二维矩阵

0 阅读3分钟

给你一个满足下述两条属性的 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

1. 生活案例:在电影院找座位

想象你在一个电影院里:

  • 布局:电影院有 mm 排,每排有 nn 个座位。

  • 规律:票号是连续的。第一排是 17 号,第二排是 1020 号……每一排都比前一排的号大。

  • 现状:你手里有一个票号(target),你想知道这个票号在不在这个影院里。

  • 笨办法:一排一排地找。

  • 聪明办法:因为票号是整体递增的,你可以把整个电影院看成一条长长的直线队伍

    1. 你先算出一共有多少个座位(m×nm \times n)。
    2. 用二分法在这一条长线上找。
    3. 当你确定是“第几个”座位时,通过简单的除法和取余,就能算出它在第几排、第几列

2. 代码实现与详细注释

这是你图片中的代码,我为你加上了“坐标转换”逻辑的详细中文注释:

JavaScript

/**
 * @param {number[][]} matrix - 二维矩阵(影院布局)
 * @param {number} target - 目标值(你的票号)
 * @return {boolean}
 */
var searchMatrix = function(matrix, target) {
    let m = matrix.length;       // 行数
    let n = matrix[0].length;    // 每行有多少个座位(列数)
    
    let left = 0;
    let right = m * n - 1;       // 把二维拉平,最后一个座位的索引

    while (left <= right) {
        // 1. 找到中间那个座位的“长线索引”
        let mid = Math.floor(left + (right - left) / 2);
        
        // 2. 【核心坐标转换】:
        // row = index / 每行人数 (取整)
        // col = index % 每行人数 (取余)
        let row = Math.floor(mid / n);
        let col = mid % n;
        
        let value = matrix[row][col]; // 找到电影院里对应的真实座位号

        if (value === target) {
            return true; // 找到了!
        } else if (value < target) {
            // 中间的号小了,往后找
            left = mid + 1;
        } else {
            // 中间的号大了,往前找
            right = mid - 1;
        }
    }

    return false; // 找遍了,没这个票号
};

3. 核心原理解析

为什么可以这样做?

题目给出了两个关键条件:

  1. 每行从左到右递增。

  2. 每行的第一个数大于前一行的最后一个数。

    这两个条件加在一起,意味着这个二维矩阵本质上就是一个排好序的一维数组,只是为了方便存储被“折断”成了几行。

坐标转换公式

如果一个二维矩阵有 nn 列,那么一维索引 idxidx 对应的二维位置为:

  • 行号row=idx/nrow = \lfloor idx / n \rfloor
  • 列号col=idx(modn)col = idx \pmod n

这个公式是处理矩阵类算法题的“金钥匙”。

复杂度分析

  • 时间复杂度O(log(m×n))O(\log(m \times n))。这比先找行再找列的两次二分更加直接高效。
  • 空间复杂度O(1)O(1)。只用了几个定义位置的变量。