LeetCode 热题 100-----21.搜索二维矩阵 II

一、题目完整解读

大家先不用怕"二维矩阵""算法"这些听起来高深的词,我们用最通俗的话把题目讲明白,保证完全没接触过编程的人也能懂。

题目给我们一个"表格"(就是二维矩阵),这个表格有两个固定规律,一定要记牢:

  • 规律1:每一行的数字,从左到右是逐渐变大的(比如第一行 [1,4,7,11,15],1<4<7<11<15);

  • 规律2:每一列的数字,从上到下是逐渐变大的(比如第一列 [1,2,3,10,18],1<2<3<10<18)。

我们的任务很简单:给一个目标数字(target),写一段代码,判断这个数字是不是在这个"表格"里。如果在,就返回"真"(True/true);如果不在,就返回"假"(False/false)。

举两个具体例子,帮大家理解:

示例1:表格是 [[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]],目标数字是5。我们能在表格第二行第二列找到5,所以返回True。

示例2:还是同一个表格,目标数字是20。整个表格里没有20,所以返回False。

题目额外说明(不用死记,了解即可):表格的行数(m)和列数(n)都是1到300之间,数字的范围很大(-10⁹到10⁹),但我们不用关心这个,代码会自动处理。

二、前置知识

在看代码之前,必须先掌握3个核心基础知识点,每一个都讲得明明白白,不会跳过任何一个细节,放心看!

知识点1:什么是二维矩阵(表格)?如何访问表格里的数字?

我们生活中常见的Excel表格、课堂上的座位表,都是"二维矩阵"。在编程里,它就是"列表套列表"(Python)或"向量套向量"(C++),可以理解成"一行一行的列表,拼在一起组成一个大列表"。

举个例子(和题目示例一致):

Python里的二维矩阵:matrix = [[1,4,7,11,15], [2,5,8,12,19], [3,6,9,16,22]]

C++里的二维矩阵:vector<vector<int>> matrix = {{1,4,7,11,15}, {2,5,8,12,19}, {3,6,9,16,22}};

怎么访问表格里的某个数字?------ 用"行号+列号"定位,记为 matrix[行号][列号](重点!)

注意:编程里的"行号""列号"都是从0开始算的(不是从1开始!),比如:

  • matrix[0][0]:第0行(第一行)、第0列(第一列),数字是1;

  • matrix[1][1]:第1行(第二行)、第1列(第二列),数字是5;

  • matrix[2][3]:第2行(第三行)、第3列(第四列),数字是16。

补充:如何获取表格的行数和列数?

  • Python:行数 = len(matrix)(大列表的长度,就是有多少个小列表);列数 = len(matrix[0])(第一个小列表的长度,就是一行有多少个数字);

  • C++:行数 = matrix.size()(向量的size()方法,获取大向量里有多少个小向量);列数 = matrix[0].size()(第一个小向量的size(),获取一行有多少个数字)。

知识点2:循环遍历(如何"挨个查看"表格里的所有数字)

我们要判断目标数字在不在表格里,最直接的方式就是"挨个看",这就需要用到"循环"------ 重复做一件事(查看数字),直到所有数字都看完。

因为是二维表格(行+列),所以需要"嵌套循环":先遍历每一行,再遍历当前行的每一个数字(每一列)。

举个通俗例子:像老师查座位表,先查第一行(从左到右查每个学生),再查第二行,直到所有行查完。

两种语言的循环写法(先有个印象,后面代码里会详细讲):

  • Python:用for循环,先遍历每一行(for row in matrix),再遍历当前行的每一个数字(for num in row);

  • C++:用for循环,先定义行号i(从0到行数-1),再定义列号j(从0到列数-1),通过matrix[i][j]访问每个数字。

知识点3:条件判断(如何判断"当前数字是不是目标值")

遍历到一个数字后,我们需要判断它是不是我们要找的目标值(target),这就需要用到"条件判断"------ if-else语句。

核心逻辑(非常简单):

  • 如果 当前数字 == 目标值 → 找到目标,直接返回"真"(True/true),不用再继续查看;

  • 如果 当前数字 != 目标值 → 继续查看下一个数字;

  • 如果所有数字都查完了,还是没有找到 → 返回"假"(False/false)。

补充:边界判断(避免出错的关键)

如果表格是空的(比如没有任何行,或者只有一行但没有任何数字),直接返回False,因为里面不可能有目标值------ 这一步很重要,能避免代码报错。

知识点4:补充(Python/C++ 基础语法,必备)

为了让大家看懂代码,再补充几个基础语法,不用死记,跟着代码对照看即可:

  • Python:

    • from typing import List:导入列表类型,刷题时必须写,用来声明函数参数的类型(告诉代码,matrix是"列表套列表",target是整数);

    • class Solution: 定义一个"解决方案类",刷题时固定写法,所有解题函数都放在这个类里面;

    • def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: 定义解题函数,self是类的固定参数,后面两个是输入(matrix和target),-> bool表示函数返回值是"真/假"。

  • C++:

    • #include <vector>:导入向量(vector)库,因为二维矩阵用vector实现,必须写;

    • using namespace std;:简化代码写法,不用每次都写"std::vector",直接写"vector"即可;

    • class Solution: 定义解决方案类,固定写法;

    • bool searchMatrix(vector<vector<int>>& matrix, int target):定义解题函数,返回值是bool(真/假),参数matrix是"vector套vector",&表示"引用传递"(不用复制整个矩阵,更高效),target是目标值。

三、解法一:暴力遍历法

1. 核心思路(能直接理解)

完全不利用题目中"每行、每列升序"的规律,就是"挨个查看"表格里的每一个数字,直到找到目标值,或者查完所有数字。

步骤拆解:

  1. 先判断表格是不是空的(边界判断),如果是空的,直接返回False;

  2. 遍历表格的每一行;

  3. 遍历当前行的每一个数字;

  4. 如果当前数字 == 目标值 → 返回True;

  5. 所有数字都查完,没找到 → 返回False。

优点:代码最简单,不用思考任何规律,零基础能快速写出来;

缺点:效率低,最坏情况下要查完所有数字(比如目标值在最后一个位置,或者不在表格里),时间复杂度是O(m×n)(m是行数,n是列数)。

2. Python 暴力解法(逐句注释,每句都讲清含义)

python 复制代码
# 导入列表类型声明(刷题必备,告诉代码"List"是什么,避免报错)
from typing import List

# 定义解决方案类(刷题固定写法,所有解题函数都放在这个类里)
class Solution:
    # 定义解题函数:searchMatrix是函数名,self是类的固定参数
    # matrix: List[List[int]] → 声明输入的matrix是"列表套列表"(二维矩阵),里面的元素是整数
    # target: int → 声明输入的target是整数
    # -> bool → 声明函数的返回值是"真/假"(True/False)
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        # 边界判断1:如果矩阵是空的(没有任何行),直接返回False(里面不可能有目标值)
        if not matrix:
            return False
        # 边界判断2:如果矩阵有行,但第一行是空的(没有任何列),也返回False
        if not matrix[0]:
            return False
        
        # 遍历矩阵的每一行:row代表当前行(比如第一次循环,row是[1,4,7,11,15])
        for row in matrix:
            # 遍历当前行的每一个数字:num代表当前行的某个数字(比如第一次循环,num是1,然后是4、7...)
            for num in row:
                # 条件判断:如果当前数字等于目标值,说明找到了,直接返回True
                if num == target:
                    return True
        # 循环结束:所有行、所有数字都遍历完了,还是没找到目标值,返回False
        return False

# 主函数(测试用,可以直接运行,查看结果)
# 主函数的作用:定义测试数据,调用上面的解题函数,输出结果
if __name__ == "__main__":
    # 实例化Solution类(创建一个"解题工具",才能调用里面的searchMatrix函数)
    sol = Solution()
    
    # 测试用例1(题目给定的示例1)
    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]
    ]
    target1 = 5
    # 调用解题函数,传入测试数据,获取结果
    result1 = sol.searchMatrix(matrix1, target1)
    # 输出结果:打印"测试用例1"和对应的结果,让我们能看到是否正确
    print(f"测试用例1:matrix={matrix1}, target={target1}")
    print(f"测试结果1:{result1}(正确结果应为True)\n")
    
    # 测试用例2(题目给定的示例2)
    matrix2 = [
        [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]
    ]
    target2 = 20
    result2 = sol.searchMatrix(matrix2, target2)
    print(f"测试用例2:matrix={matrix2}, target={target2}")
    print(f"测试结果2:{result2}(正确结果应为False)\n")
    
    # 自定义测试用例3(空矩阵,测试边界)
    matrix3 = []  # 没有任何行的空矩阵
    target3 = 10
    result3 = sol.searchMatrix(matrix3, target3)
    print(f"自定义测试用例3:matrix={matrix3}, target={target3}")
    print(f"测试结果3:{result3}(正确结果应为False)\n")
    
    # 自定义测试用例4(只有一行一列的矩阵)
    matrix4 = [[7]]  # 只有一行、一列,数字是7
    target4 = 7
    result4 = sol.searchMatrix(matrix4, target4)
    print(f"自定义测试用例4:matrix={matrix4}, target={target4}")
    print(f"测试结果4:{result4}(正确结果应为True)\n")
    
    # 自定义测试用例5(目标值比所有数字都小)
    matrix5 = [[2,4,6],[3,5,7]]
    target5 = 1
    result5 = sol.searchMatrix(matrix5, target5)
    print(f"自定义测试用例5:matrix={matrix5}, target={target5}")
    print(f"测试结果5:{result5}(正确结果应为False)\n")
    
    # 自定义测试用例6(目标值比所有数字都大)
    matrix6 = [[2,4,6],[3,5,7]]
    target6 = 8
    result6 = sol.searchMatrix(matrix6, target6)
    print(f"自定义测试用例6:matrix={matrix6}, target={target6}")
    print(f"测试结果6:{result6}(正确结果应为False)")

3. Python 代码调用流程 & 运行过程(逐步看懂)

(1)调用流程(怎么用这个代码)

  1. 先导入List类型(from typing import List),确保代码能识别"二维列表";

  2. 定义Solution类,里面包含解题函数searchMatrix;

  3. 在主函数(if name == "main":)里,创建Solution的实例sol(相当于"打开解题工具");

  4. 定义测试用例(matrix和target);

  5. 调用sol.searchMatrix(matrix, target),传入测试数据,获取返回结果;

  6. 打印结果,查看是否正确。

(2)运行过程(以测试用例1为例,逐步看代码怎么执行)

测试用例1:matrix1是5行5列的矩阵,target1=5

  1. 执行边界判断:matrix1不是空的,matrix1[0](第一行)也不是空的,所以不返回False;

  2. 进入第一个for循环,遍历第一行row = [1,4,7,11,15];

  3. 进入第二个for循环,遍历第一行的数字:

    1. num=1 → 1 !=5 → 继续;

    2. num=4 → 4 !=5 → 继续;

    3. num=7 →7 !=5 → 继续;

    4. num=11 →11 !=5 → 继续;

    5. num=15 →15 !=5 → 第一行遍历结束,进入下一行。

  4. 遍历第二行row = [2,5,8,12,19];

  5. 进入第二个for循环,遍历第二行的数字:

    1. num=2 →2 !=5 → 继续;

    2. num=5 →5 ==5 → 满足条件,直接返回True;

  6. 函数返回True,主函数接收结果,打印"测试结果1:True",整个运行结束。

补充:如果测试用例2(target=20),代码会遍历完所有5行5列的数字(共25个),都没找到20,最后返回False。

4. C++ 暴力解法(逐句注释,每句都讲清含义)

cpp 复制代码
#include <vector>  // 导入向量库,因为二维矩阵用vector(向量)实现,必须写
using namespace std;  // 简化语法,不用每次写"std::vector",直接写"vector"

// 定义解决方案类(刷题固定写法)
class Solution {
public:
    // 定义解题函数:返回值是bool(真/假)
    // vector<vector<int>>& matrix → 输入的二维矩阵,&表示"引用传递"(高效,不复制整个矩阵)
    // int target → 输入的目标值(整数)
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        // 边界判断1:如果矩阵是空的(没有任何行),返回False
        if (matrix.empty()) {
            return false;
        }
        // 边界判断2:如果矩阵有行,但第一行是空的(没有任何列),返回False
        if (matrix[0].empty()) {
            return false;
        }
        
        // 获取矩阵的行数m:matrix.size() → 大向量的长度(有多少个小向量,就是多少行)
        int m = matrix.size();
        // 获取矩阵的列数n:matrix[0].size() → 第一个小向量的长度(一行有多少个数字,就是多少列)
        int n = matrix[0].size();
        
        // 外层for循环:遍历每一行,i是行号(从0到m-1,因为行号从0开始)
        for (int i = 0; i < m; i++) {
            // 内层for循环:遍历当前行的每一列,j是列号(从0到n-1)
            for (int j = 0; j < n; j++) {
                // 访问当前行、当前列的数字:matrix[i][j]
                // 条件判断:如果当前数字等于目标值,返回true
                if (matrix[i][j] == target) {
                    return true;
                }
            }
        }
        // 所有数字都遍历完,没找到目标值,返回false
        return false;
    }
};

// 主函数(测试用,可以直接复制运行,查看结果)
// 主函数是程序的入口,代码从这里开始执行
int main() {
    // 实例化Solution类,创建解题对象sol(相当于"打开解题工具")
    Solution sol;
    
    // 测试用例1(题目给定的示例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;
    // 调用解题函数,传入测试数据,获取结果(result1是bool类型,true/false)
    bool result1 = sol.searchMatrix(matrix1, target1);
    // 输出结果:cout是C++的打印语句,endl是换行
    cout << "测试用例1:" << endl;
    cout << "matrix:" << endl;
    // 打印矩阵(方便查看测试数据)
    for (auto row : matrix1) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target1 << endl;
    cout << "测试结果1:" << (result1 ? "true" : "false") << "(正确结果应为true)" << endl << endl;
    
    // 测试用例2(题目给定的示例2)
    vector<vector<int>> matrix2 = {
        {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 target2 = 20;
    bool result2 = sol.searchMatrix(matrix2, target2);
    cout << "测试用例2:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix2) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target2 << endl;
    cout << "测试结果2:" << (result2 ? "true" : "false") << "(正确结果应为false)" << endl << endl;
    
    // 自定义测试用例3(空矩阵)
    vector<vector<int>> matrix3;  // 空矩阵(没有任何行)
    int target3 = 10;
    bool result3 = sol.searchMatrix(matrix3, target3);
    cout << "自定义测试用例3:" << endl;
    cout << "matrix:空矩阵" << endl;
    cout << "target:" << target3 << endl;
    cout << "测试结果3:" << (result3 ? "true" : "false") << "(正确结果应为false)" << endl << endl;
    
    // 自定义测试用例4(只有一行一列的矩阵)
    vector<vector<int>> matrix4 = {{7}};
    int target4 = 7;
    bool result4 = sol.searchMatrix(matrix4, target4);
    cout << "自定义测试用例4:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix4) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target4 << endl;
    cout << "测试结果4:" << (result4 ? "true" : "false") << "(正确结果应为true)" << endl << endl;
    
    // 自定义测试用例5(目标值比所有数字都小)
    vector<vector<int>> matrix5 = {{2,4,6},{3,5,7}};
    int target5 = 1;
    bool result5 = sol.searchMatrix(matrix5, target5);
    cout << "自定义测试用例5:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix5) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target5 << endl;
    cout << "测试结果5:" << (result5 ? "true" : "false") << "(正确结果应为false)" << endl << endl;
    
    // 自定义测试用例6(目标值比所有数字都大)
    vector<vector<int>> matrix6 = {{2,4,6},{3,5,7}};
    int target6 = 8;
    bool result6 = sol.searchMatrix(matrix6, target6);
    cout << "自定义测试用例6:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix6) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target6 << endl;
    cout << "测试结果6:" << (result6 ? "true" : "false") << "(正确结果应为false)" << endl;
    
    return 0;  // 主函数结束,返回0表示程序正常运行
}

5. C++ 代码调用流程 & 运行过程(逐步看懂)

(1)调用流程(怎么用这个代码)

  1. 导入vector库(#include &lt;vector&gt;),否则无法使用二维矩阵;

  2. 写using namespace std;,简化代码写法;

  3. 定义Solution类,里面包含解题函数searchMatrix;

  4. 主函数main()是程序的入口,代码从这里开始执行;

  5. 在main()里,创建Solution的实例sol(打开解题工具);

  6. 定义测试用例(vector<vector<int>>类型的matrix和int类型的target);

  7. 调用sol.searchMatrix(matrix, target),传入测试数据,获取返回结果(bool类型);

  8. 用cout打印测试数据和结果,查看是否正确;

  9. 主函数返回0,程序正常结束。

(2)运行过程(以测试用例1为例,逐步看代码怎么执行)

测试用例1:matrix1是5行5列的矩阵,target1=5

  1. 执行边界判断:matrix1不是空的(matrix.empty()为false),matrix1[0]也不是空的(matrix[0].empty()为false),所以不返回false;

  2. 获取行数m=5(matrix1.size()=5),列数n=5(matrix1[0].size()=5);

  3. 外层for循环:i从0开始(i=0,第一行);

  4. 内层for循环:j从0开始(j=0,第一列),访问matrix1[0][0]=1 → 1 !=5 → j=1,matrix1[0][1]=4 →4 !=5 → j=2,matrix1[0][2]=7 →7 !=5 → j=3,matrix1[0][3]=11 →11 !=5 → j=4,matrix1[0][4]=15 →15 !=5 → 内层循环结束,i=1(第二行);

  5. 内层for循环:j=0,matrix1[1][0]=2 →2 !=5 → j=1,matrix1[1][1]=5 →5 ==5 → 满足条件,返回true;

  6. 主函数接收result1=true,打印"测试结果1:true",程序继续执行其他测试用例,最后返回0,结束运行。

四、解法二:高效查找法

1. 核心思路(利用矩阵规律,大幅提速)

这是这道题的最优解法,核心是"利用题目中'每行升序、每列升序'的规律,每次排除一行或一列,不用遍历所有数字",效率大幅提升。

关键技巧:选择「矩阵右上角的数字」作为查找的起点(为什么选这里?因为这个数字有个天然优势):

👉 右上角的数字 = 当前行的最大值(因为行是升序,最右边最大) + 当前列的最小值(因为列是升序,最上边最小)

基于这个优势,我们可以每次排除一行或一列,快速缩小查找范围,步骤拆解(必看,结合例子理解):

  1. 边界判断:如果矩阵是空的,直接返回False;

  2. 初始化起点:行号row=0(第一行),列号col=矩阵列数-1(最后一列)→ 也就是右上角的数字;

  3. 循环条件:行号row不超过总行数(没越界),列号col不小于0(没越界);

  4. 获取当前起点的数字current = matrix[row][col];

  5. 三种情况:

    1. 情况1:current == target → 找到目标值,返回True;

    2. 情况2:current > target → 目标值一定在当前数字的左边(因为当前列是升序,当前数字是列的最小值,左边的数字都比它小),所以列号col减1(向左移一列,排除当前列);

    3. 情况3:current < target → 目标值一定在当前数字的下边(因为当前行是升序,当前数字是行的最大值,下边的数字都比它大),所以行号row加1(向下移一行,排除当前行);

  6. 如果循环结束(行或列越界),还没找到目标值 → 返回False。

举个通俗例子(测试用例1,target=5):

矩阵右上角是matrix[0][4] =15 → 15>5 → 列号减1(col=3),排除第4列;

当前数字是matrix[0][3]=11 →11>5 → 列号减1(col=2),排除第3列;

当前数字是matrix[0][2]=7 →7>5 → 列号减1(col=1),排除第2列;

当前数字是matrix[0][1]=4 →4<5 → 行号加1(row=1),排除第0行;

当前数字是matrix[1][1]=5 →5==5 → 返回True。

优点:效率极高,时间复杂度是O(m+n)(最多走m+n步,比如从右上角走到左下角),是面试时的首选解法;

缺点:需要理解"右上角起点"的逻辑,比暴力解法稍难一点,但只要记住"大左移、小下移",就能轻松写出来。

2. Python 最优解法(逐句注释,每句都讲清含义)

python 复制代码
# 导入列表类型声明(刷题必备)
from typing import List

# 定义解决方案类(固定写法)
class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        # 边界判断1:如果矩阵是空的(没有任何行),返回False
        if not matrix:
            return False
        # 边界判断2:如果矩阵有行,但第一行是空的(没有任何列),返回False
        if not matrix[0]:
            return False
        
        # 初始化起点:右上角(第0行,最后一列)
        row = 0  # 行号,从第一行(0行)开始
        col = len(matrix[0]) - 1  # 列号,从最后一列开始(len(matrix[0])是列数,减1是因为列号从0开始)
        rows = len(matrix)  # 总行数,用来判断行号是否越界
        
        # 循环条件:行号row < 总行数(没越界),列号col >=0(没越界)
        while row < rows and col >= 0:
            # 获取当前起点的数字(右上角开始,每次更新)
            current = matrix[row][col]
            
            # 情况1:当前数字等于目标值,找到目标,返回True
            if current == target:
                return True
            # 情况2:当前数字大于目标值 → 目标值在左边,列号减1(向左移一列)
            elif current > target:
                col -= 1
            # 情况3:当前数字小于目标值 → 目标值在下边,行号加1(向下移一行)
            else:
                row += 1
        
        # 循环结束:行或列越界,没找到目标值,返回False
        return False

# 主函数(测试用,和暴力解法的主函数结构一致,测试所有案例)
if __name__ == "__main__":
    sol = Solution()
    
    # 测试用例1(题目示例1)
    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]
    ]
    target1 = 5
    result1 = sol.searchMatrix(matrix1, target1)
    print(f"测试用例1:matrix={matrix1}, target={target1}")
    print(f"测试结果1:{result1}(正确结果应为True)\n")
    
    # 测试用例2(题目示例2)
    matrix2 = [
        [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]
    ]
    target2 = 20
    result2 = sol.searchMatrix(matrix2, target2)
    print(f"测试用例2:matrix={matrix2}, target={target2}")
    print(f"测试结果2:{result2}(正确结果应为False)\n")
    
    # 自定义测试用例3(空矩阵)
    matrix3 = []
    target3 = 10
    result3 = sol.searchMatrix(matrix3, target3)
    print(f"自定义测试用例3:matrix={matrix3}, target={target3}")
    print(f"测试结果3:{result3}(正确结果应为False)\n")
    
    # 自定义测试用例4(只有一行一列)
    matrix4 = [[7]]
    target4 = 7
    result4 = sol.searchMatrix(matrix4, target4)
    print(f"自定义测试用例4:matrix={matrix4}, target={target4}")
    print(f"测试结果4:{result4}(正确结果应为True)\n")
    
    # 自定义测试用例5(目标值在左上角)
    matrix5 = [[1,2,3],[4,5,6],[7,8,9]]
    target5 = 1
    result5 = sol.searchMatrix(matrix5, target5)
    print(f"自定义测试用例5:matrix={matrix5}, target={target5}")
    print(f"测试结果5:{result5}(正确结果应为True)\n")
    
    # 自定义测试用例6(目标值在右下角)
    matrix6 = [[1,2,3],[4,5,6],[7,8,9]]
    target6 = 9
    result6 = sol.searchMatrix(matrix6, target6)
    print(f"自定义测试用例6:matrix={matrix6}, target={target6}")
    print(f"测试结果6:{result6}(正确结果应为True)")

3. Python 最优解法 调用流程 & 运行过程(逐步看懂)

(1)调用流程

和暴力解法的调用流程完全一致:导入List → 定义Solution类 → 主函数创建实例 → 定义测试用例 → 调用函数 → 打印结果。

(2)运行过程(以测试用例1为例,逐步拆解)

测试用例1:matrix1=5行5列,target1=5

  1. 边界判断:matrix1非空,matrix1[0]非空,不返回False;

  2. 初始化:row=0(第一行),col=5-1=4(最后一列),rows=5(总行数);

  3. 进入while循环(row=0 <5,col=4 ≥0,满足条件):

    1. current = matrix[0][4] =15 → 15>5 → 执行col -=1 → col=3;
  4. 循环继续(row=0 <5,col=3 ≥0):

    1. current = matrix[0][3] =11 →11>5 → col -=1 → col=2;
  5. 循环继续(row=0 <5,col=2 ≥0):

    1. current = matrix[0][2] =7 →7>5 → col -=1 → col=1;
  6. 循环继续(row=0 <5,col=1 ≥0):

    1. current = matrix[0][1] =4 →4<5 → row +=1 → row=1;
  7. 循环继续(row=1 <5,col=1 ≥0):

    1. current = matrix[1][1] =5 →5==5 → 返回True;
  8. 函数返回True,主函数打印结果,运行结束。

补充(测试用例2,target=20):

从右上角15开始,逐步左移、下移,最后row=5(超过总行数4),循环结束,返回False,全程只走了8步(比暴力解法的25步少很多)。

4. C++ 最优解法(逐句注释,每句都讲清含义)

cpp 复制代码
#include <vector>
using namespace std;

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        // 边界判断1:矩阵为空(没有任何行),返回false
        if (matrix.empty()) {
            return false;
        }
        // 边界判断2:第一行为空(没有任何列),返回false
        if (matrix[0].empty()) {
            return false;
        }
        
        // 初始化起点:右上角(第0行,最后一列)
        int row = 0;  // 行号,从第0行开始
        int col = matrix[0].size() - 1;  // 列号,从最后一列开始(size()-1是因为列号从0开始)
        int rows = matrix.size();  // 总行数,用于判断行号是否越界
        
        // 循环条件:行号不越界(row < rows),列号不越界(col >= 0)
        while (row < rows && col >= 0) {
            // 获取当前起点的数字
            int current = matrix[row][col];
            
            // 情况1:找到目标值,返回true
            if (current == target) {
                return true;
            }
            // 情况2:当前数字大于目标值 → 向左移一列(col减1),排除当前列
            else if (current > target) {
                col--;
            }
            // 情况3:当前数字小于目标值 → 向下移一行(row加1),排除当前行
            else {
                row++;
            }
        }
        
        // 循环结束,没找到目标值,返回false
        return false;
    }
};

// 主函数(测试用,和暴力解法主函数结构一致,可直接复制运行)
int main() {
    // 实例化Solution类,创建解题对象sol(打开解题工具)
    Solution sol;
    
    // 测试用例1(题目给定的示例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;
    // 调用最优解法的解题函数,传入测试数据,获取结果
    bool result1 = sol.searchMatrix(matrix1, target1);
    // 打印测试数据和结果,方便查看
    cout << "测试用例1:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix1) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target1 << endl;
    cout << "测试结果1:" << (result1 ? "true" : "false") << "(正确结果应为true)" << endl << endl;
    
    // 测试用例2(题目给定的示例2)
    vector<vector<int>> matrix2 = {
        {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 target2 = 20;
    bool result2 = sol.searchMatrix(matrix2, target2);
    cout << "测试用例2:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix2) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target2 << endl;
    cout << "测试结果2:" << (result2 ? "true" : "false") << "(正确结果应为false)" << endl << endl;
    
    // 自定义测试用例3(空矩阵,测试边界)
    vector<vector<int>> matrix3;
    int target3 = 10;
    bool result3 = sol.searchMatrix(matrix3, target3);
    cout << "自定义测试用例3:" << endl;
    cout << "matrix:空矩阵" << endl;
    cout << "target:" << target3 << endl;
    cout << "测试结果3:" << (result3 ? "true" : "false") << "(正确结果应为false)" << endl << endl;
    
    // 自定义测试用例4(只有一行一列的矩阵)
    vector<vector<int>> matrix4 = {{7}};
    int target4 = 7;
    bool result4 = sol.searchMatrix(matrix4, target4);
    cout << "自定义测试用例4:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix4) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target4 << endl;
    cout << "测试结果4:" << (result4 ? "true" : "false") << "(正确结果应为true)" << endl << endl;
    
    // 自定义测试用例5(目标值在左上角)
    vector<vector<int>> matrix5 = {{1,2,3},{4,5,6},{7,8,9}};
    int target5 = 1;
    bool result5 = sol.searchMatrix(matrix5, target5);
    cout << "自定义测试用例5:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix5) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target5 << endl;
    cout << "测试结果5:" << (result5 ? "true" : "false") << "(正确结果应为true)" << endl << endl;
    
    // 自定义测试用例6(目标值在右下角)
    vector<vector<int>> matrix6 = {{1,2,3},{4,5,6},{7,8,9}};
    int target6 = 9;
    bool result6 = sol.searchMatrix(matrix6, target6);
    cout << "自定义测试用例6:" << endl;
    cout << "matrix:" << endl;
    for (auto row : matrix6) {
        for (auto num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    cout << "target:" << target6 << endl;
    cout << "测试结果6:" << (result6 ? "true" : "false") << "(正确结果应为true)" << endl;
    
    return 0;  // 主函数结束,返回0表示程序正常运行
}

5. C++ 最优解法 调用流程 & 运行过程(逐步看懂)

(1)调用流程

和C++暴力解法的调用流程完全一致,步骤如下,对照代码看就能懂:

  1. 导入vector库(#include <vector>),确保能使用二维矩阵(vector套vector);

  2. 写using namespace std;,简化代码,不用每次都写"std::"前缀;

  3. 定义Solution类,里面包含最优解法的解题函数searchMatrix;

  4. 主函数main()是程序入口,代码从这里开始执行;

  5. 在main()里,创建Solution的实例sol(相当于打开解题工具,才能调用里面的函数);

  6. 定义6个测试用例(和暴力解法完全一致,覆盖正常情况、边界情况);

  7. 调用sol.searchMatrix(matrix, target),传入测试数据,获取返回结果(bool类型,true/false);

  8. 用cout打印测试数据(矩阵、目标值)和测试结果,方便查看是否正确;

  9. 主函数返回0,程序正常结束。

(2)运行过程(以测试用例1为例,逐步拆解,能跟着走)

测试用例1:matrix1是5行5列矩阵,target1=5,核心是"大左移、小下移",逐步缩小范围

  1. 边界判断:matrix1非空(matrix.empty()为false),matrix1[0]非空(matrix[0].empty()为false),不返回false;

  2. 初始化起点:row=0(第一行),col=matrix1[0].size()-1=5-1=4(最后一列),rows=matrix1.size()=5(总行数);

  3. 进入while循环(row=0 <5,col=4 ≥0,满足循环条件): current = matrix1[0][4] = 15 → 15 > 5(当前数字大于目标值);

  4. 执行col-- → col=3(向左移一列,排除第4列,因为第4列所有数字都比15大,不可能有5);

  5. 循环继续(row=0 <5,col=3 ≥0): current = matrix1[0][3] = 11 → 11 > 5;

  6. 执行col-- → col=2(向左移一列,排除第3列);

  7. 循环继续(row=0 <5,col=2 ≥0): current = matrix1[0][2] = 7 → 7 > 5;

  8. 执行col-- → col=1(向左移一列,排除第2列);

  9. 循环继续(row=0 <5,col=1 ≥0): current = matrix1[0][1] = 4 → 4 < 5(当前数字小于目标值);

  10. 执行row++ → row=1(向下移一行,排除第0行,因为第0行所有数字都比4小,不可能有5);

  11. 循环继续(row=1 <5,col=1 ≥0): current = matrix1[1][1] = 5 → 5 == 5(找到目标值);

  12. 直接返回true,跳出循环和函数;

  13. 主函数接收result1=true,打印"测试结果1:true",然后继续执行其他测试用例,全部执行完后返回0,程序结束。

补充(测试用例2,target=20):

运行过程拆解(帮理解"排除行/列"的优势):

  1. 初始化row=0,col=4,current=15 → 15 <20 → row++(row=1);

  2. current=matrix1[1][4]=19 →19 <20 → row++(row=2);

  3. current=matrix1[2][4]=22 →22 &gt;20 → col--(col=3);

  4. current=matrix1[2][3]=16 →16 <20 → row++(row=3);

  5. current=matrix1[3][3]=17 →17 <20 → row++(row=4);

  6. current=matrix1[4][3]=26 →26 >20 → col--(col=2);

  7. current=matrix1[4][2]=23 →23 >20 → col--(col=1);

  8. current=matrix1[4][1]=21 →21 >20 → col--(col=0);

  9. current=matrix1[4][0]=18 →18 <20 → row++(row=5);

  10. 此时row=5 ≥ rows=5,循环结束,返回false(全程仅8步,远少于暴力解法的25步)。

五、两种解法对比(必看,清晰区分)

为了让快速分清两种解法的区别,整理了清晰的对比,不用死记,理解即可:

对比维度 解法一:暴力遍历法 解法二:高效查找法(最优解)
核心逻辑 不利用矩阵规律,挨个遍历所有数字,逐个判断 利用"每行/每列升序"规律,从右上角开始,每次排除一行或一列
难度 极低,零基础能直接写,不用思考规律 中等,记住"大左移、小下移"即可,理解后很简单
效率(时间复杂度) 低,O(m×n),最坏情况遍历所有数字(m行n列) 高,O(m+n),最多走m+n步(从右上角走到左下角)
适用场景 新手入门、矩阵规模很小(比如1-2行/列) 面试、矩阵规模大(题目中300行300列,优先选这个)
关键技巧 嵌套循环,逐一遍历 右上角为起点,"大左移、小下移",排除法缩小范围

六、常见问题解答

整理了学习过程中最容易问的4个问题,每个问题都用通俗的话解答,避免踩坑:

疑问1:为什么行号、列号要从0开始,而不是从1开始?

这是编程的通用约定,不是这道题特有的!就像我们排队,从第0个位置开始数,和生活中"第1个"对应。比如matrix[0][0],生活中是"第一行第一列",编程里写"0行0列",记住这个对应关系就不会错。

疑问2:高效解法为什么选右上角当起点?选左上角、右下角不行吗?

核心原因:只有右上角(或左下角)的数字,能同时实现"排除一行或一列",其他位置不行,举个例子说明:

  • 选左上角:数字是当前行最小、当前列最小,比如matrix[0][0]=1,比目标值5小,只能向下移,但无法排除一行/一列,还是要遍历很多数字;

  • 选右下角:数字是当前行最大、当前列最大,比如matrix[4][4]=30,比目标值5大,只能向左移,也无法排除一行/一列;

  • 选右上角:数字是当前行最大、当前列最小,能直接判断"大了左移、小了下移",每次必排除一行或一列,效率最高。

疑问3:代码里的"边界判断"为什么必须写?不写会怎么样?

必须写!不写会导致代码报错,比如:

  • 如果矩阵是空的(matrix=[]),执行len(matrix[0])(Python)或matrix[0].size()(C++)时,会报错"访问了不存在的元素";

  • 边界判断能提前"拦截"空矩阵的情况,直接返回false,避免代码崩溃,这是写算法题的好习惯,一定要记住。

疑问4:Python和C++的代码逻辑一样,为什么语法不一样?

因为Python和C++是两种不同的编程语言,就像"中文和英文",表达同一个意思(解题逻辑),但用词、句式不一样。比如:

  • Python用len(matrix)获取行数,C++用matrix.size();

  • Python用for row in matrix遍历行,C++用for循环+行号i遍历;

  • 但核心逻辑(边界判断、遍历/排除思路、条件判断)完全一致,学会一种,另一种只要记语法差异就能快速掌握。

七、总结(必看,快速梳理重点)

这道题的核心是"利用矩阵的升序规律,优化查找效率",不用害怕,按以下步骤学习,就能完全掌握:

  1. 先掌握前置知识:理解二维矩阵、循环遍历、条件判断,这是基础,没掌握就先看第二部分,不要急着看代码;

  2. 先写暴力解法:不用想规律,先实现"挨个遍历",确保能正确运行,培养编程手感;

  3. 再学最优解法:记住"右上角起点+大左移、小下移",理解"每次排除一行/一列"的逻辑,这是面试重点;

  4. 多运行测试用例:把代码复制到编译器里,修改目标值、矩阵,观察运行结果,加深理解;

  5. 记住对比差异:两种解法的效率、适用场景,面试时能快速说出"暴力解法和最优解法的区别",加分项。

相关推荐
平行侠2 小时前
018二进制GCD(Stein算法)- 用位运算代替除法的最大公因数
数据结构·算法
月疯2 小时前
卡尔曼滤波的数学计算流程
算法
黎阳之光2 小时前
黎阳之光:深耕视频孪生核心领域 构筑数字孪生全域数智新标杆
大数据·人工智能·算法·安全·数字孪生
sbjdhjd2 小时前
2026年第十七届蓝桥杯大赛软件赛省赛 Python 大学 B 组 A-F 题 完整题解(小白友好版)
python·算法·职场和发展·蓝桥杯·pycharm·开源·动态规划
nlpming2 小时前
Superpowers 项目全面解析
算法
无限进步_2 小时前
【C++】红黑树完全解析:从概念到插入与平衡维护
java·c语言·开发语言·数据结构·c++·后端·算法
DaMu2 小时前
基于后天九宫八卦阵驱动的AI具身智能体联合协同指挥防御系统:架构与实现
人工智能·算法·架构
悲伤小伞2 小时前
素数筛-试除法 埃氏筛 线性筛
数据结构·算法
Chase_______3 小时前
LeetCode 2379 & 2841 题解:一文掌握定长滑动窗口的两类变体——简单计数与 HashMap 去重
算法·leetcode·职场和发展