【矩阵-2】240.搜索二维矩阵 II

题目描述:

编写一个高效的算法来搜索 m xn 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

示例 1:

复制代码
输入: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 = 5
输出:true

示例 2:

复制代码
输入: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 = 20
输出:false

解题思路:

我本来想的很简单,就是分别从左上角的横向和纵向开始往后遍历寻找,但如果对每一行遍历一遍,再对每一列遍历一遍,同一个元素会被检查两次,浪费时间。想法方向是对的 ,但需要明确一点:如果对每一行和每一列都独立遍历,那本质上还是暴力搜索,时间复杂度是 O(m × n),无法利用矩阵的单调性优势。

核心思路:从右上角开始搜索

为什么从右上角开始?

观察矩阵的特性:

  • 右上角元素 matrix[0][n-1] 是当前行最大的 ,也是当前列最小的

  • 如果 target < matrix[0][n-1]:说明 target 不可能在这一列(因为这一列下面的都更大),向左移动

  • 如果 target > matrix[0][n-1]:说明 target 不可能在这一行(因为这一行左边的都更小),向下移动

  • 如果相等:找到了

具体过程示例:

target = 5

复制代码
matrix = [
  [1,  4,  7, 11, 15],   ← 从右上角 15 开始
  [2,  5,  8, 12, 19],
  [3,  6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

第1步: 15 > 5 → 向左移动到 11
第2步: 11 > 5 → 向左移动到 7
第3步: 7 > 5  → 向左移动到 4
第4步: 4 < 5  → 向下移动到 5
第5步: 5 == 5 → 找到!返回 true

搜索路径:

text

复制代码
[15] → [11] → [7] → [4]
                      ↓
                     [5] ✅

代码实现:

cpp 复制代码
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();    // 列数
        
        int row = 0;          // 从右上角开始
        int col = n - 1;
        
        while (row < m && col >= 0) {
            if (matrix[row][col] == target) {
                return true;
            } else if (matrix[row][col] > target) {
                col--;  // 当前值太大,向左移动
            } else {
                row++;  // 当前值太小,向下移动
            }
        }
        
        return false;
    }
};

复杂度分析:

维度 复杂度 说明
时间复杂度 O(m + n) 每次移动一行或一列,最多移动 m+n 次
空间复杂度 O(1) 只用了两个指针

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

如果从左上角 matrix[0][0] 开始:

  • matrix[0][0] 是当前行最小 ,也是当前列最小

  • 如果 target > matrix[0][0],你不知道是该往右还是往下(两个方向都可能更大)

  • 无法做出唯一决策

同理,右下角也不行(它是当前行最大、当前列最大,两个方向都可能更小)。

只有右上角和左下角可以,因为它们一个是"行最大列最小",一个是"行最小列最大",能做出唯一决策。

另一种写法:从左下角开始

cpp 复制代码
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();
        
        int row = m - 1;  // 从左下角开始
        int col = 0;
        
        while (row >= 0 && col < n) {
            if (matrix[row][col] == target) {
                return true;
            } else if (matrix[row][col] > target) {
                row--;  // 当前值太大,向上移动
            } else {
                col++;  // 当前值太小,向右移动
            }
        }
        
        return false;
    }
};

两种写法完全等价。

四种起点对比:

起点 能否使用 原因
左上角 ❌ 两个方向都增大,无法决策
右上角 ✅ 左减小、下增大,可决策
左下角 ✅ 上减小、右增大,可决策
右下角 ❌ 两个方向都减小,无法决策

总结:

要点 说明
核心思想 从右上角(或左下角)开始,利用行列单调性缩小范围
时间复杂度 O(m + n)
空间复杂度 O(1)
关键决策 当前值 > target → 左移;当前值 < target → 下移
记忆口诀 "右上角出发,大了往左,小了往下。"

这道题是矩阵搜索 的经典题,核心是利用矩阵的行列单调性,每次排除一行或一列。

相关推荐
汉克老师14 分钟前
《从C++小白到算法高手:GESP 1—8级成长地图》第一讲:认识GESP——从编程新手到算法探索者
c++·gesp·信息学竞赛·小学生学c++
文丑颜不良啊1 小时前
LeetCode2861~2880题目解析
java·leetcode
岑梓铭1 小时前
《考研408数据结构》第八章(排序)复习笔记
数据结构·笔记
晴天的雨.9921 小时前
【C++】继承篇
java·开发语言·c++
青少儿编程课堂2 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会图形化考级:校园大富翁经营闯关解析
c++·python·算法·bfs·信息学竞赛
another heaven2 小时前
【C/C++ 第一次跨越(上):用 C++11/14 停止制造新的旧代码】
c语言·c++·制造
qq_406981032 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
在所不辞兄2 小时前
为什么在新能源汽车电驱与热管理开发中,AI驱动的数字孪生和数据驱动仿真正在部分替代对物理台架与实车标定的依赖?
人工智能·深度学习·神经网络·算法·机器学习
I_belong_to_jesus12 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Rabitebla14 小时前
【Linux】进程控制(十二) 自定义 Shell(二):内建命令与进程工作目录管理
linux·c++·笔记·学习·bash