欢迎光临
我们一直在努力

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. 核心思路(能直接理解)

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

步骤拆解:

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

  • 遍历表格的每一行;

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

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

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

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

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

    2. 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)调用流程(怎么用这个代码)

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

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

  • 在主函数(if __name__ == "__main__":)里,创建Solution的实例sol(相当于“打开解题工具”);

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

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

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

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

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

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

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

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

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

  • num=4 → 4 !=5 → 继续;

  • num=7 →7 !=5 → 继续;

  • num=11 →11 !=5 → 继续;

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

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

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

  • num=2 →2 !=5 → 继续;

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

  • 函数返回True,主函数接收结果,打印“测试结果1:True”,整个运行结束。

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

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

    #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)调用流程(怎么用这个代码)

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

  • 写using namespace std;,简化代码写法;

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

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

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

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

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

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

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

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

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

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

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

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

  • 内层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(第二行);

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

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

  • 四、解法二:高效查找法

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

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

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

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

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

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

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

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

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

  • 三种情况:

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

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

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

  • 如果循环结束(行或列越界),还没找到目标值 → 返回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 最优解法(逐句注释,每句都讲清含义)

    # 导入列表类型声明(刷题必备)
    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

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

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

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

  • current = matrix[0][4] =15 → 15>5 → 执行col -=1 → col=3;

  • 循环继续(row=0 <5,col=3 ≥0):

  • current = matrix[0][3] =11 →11>5 → col -=1 → col=2;

  • 循环继续(row=0 <5,col=2 ≥0):

  • current = matrix[0][2] =7 →7>5 → col -=1 → col=1;

  • 循环继续(row=0 <5,col=1 ≥0):

  • current = matrix[0][1] =4 →4<5 → row +=1 → row=1;

  • 循环继续(row=1 <5,col=1 ≥0):

  • current = matrix[1][1] =5 →5==5 → 返回True;

  • 函数返回True,主函数打印结果,运行结束。

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

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

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

    #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++暴力解法的调用流程完全一致,步骤如下,对照代码看就能懂:

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

  • 写using namespace std;,简化代码,不用每次都写“std::”前缀;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 执行col– → col=2(向左移一列,排除第3列);

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

  • 执行col– → col=1(向左移一列,排除第2列);

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

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

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

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

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

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

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

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

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

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

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

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

  • current=matrix1[4][3]=26 →26 >20 → col–(col=2);

  • current=matrix1[4][2]=23 →23 >20 → col–(col=1);

  • current=matrix1[4][1]=21 →21 >20 → col–(col=0);

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

  • 此时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遍历;

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

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

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

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

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

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

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

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

  • 赞(0)
    未经允许不得转载:171主机测评 » LeetCode 热题 100-----21.搜索二维矩阵 II
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址