剑指offer第2版——面试题4:二维数组中的查找

文章目录

一、题目

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

例如,现有矩阵 matrix 如下:

plaintext 复制代码
[
  [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 因为里头有5,返回 true;给定 target = 20因为里头没有20,返回 false

二、答案

2.1 我的答案

暴力求解法,时间复杂度较高!

cpp 复制代码
#include <iostream>
using namespace std;
/*
在一个 n * m 的二维数组中,
每一行都按照从左到右递增的顺序排序,
每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,
判断数组中是否含有该整数。
*/
template <typename T,size_t N>
bool isIn2dArray(T (&myArray)[N][N],int num)
{
    for (int i = 0; i < N; ++i)
    {
        for (int j = 0; j < N; ++j)
        {
            if (num == myArray[i][j])
            {
                return true;
            }
        }
    }
    return false;
};
int main() {
    int my_2d_array[5][5] =
    {
        {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 aim = 0;
    cin >> aim;

    cout << isIn2dArray(my_2d_array, aim) << endl;;
    

    return 0;
}

2.2 标准答案

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

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        // 处理空矩阵情况
        if (matrix.empty() || matrix[0].empty()) {
            return false;
        }

        int rows = matrix.size();
        int cols = matrix[0].size();

        // 从右上角开始查找
        int row = 0;
        int col = cols - 1;

        while (row < rows && col >= 0) {
            int current = matrix[row][col];

            if (current == target) {
                return true;
            }
            else if (current > target) {
                // 当前值大于目标值,向左移动
                col--;
            }
            else {
                // 当前值小于目标值,向下移动
                row++;
            }
        }

        // 未找到目标值
        return false;
    }
};

// 测试示例
#include <iostream>
int main() {
    Solution solution;

    // 测试用例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}
    };
    cout << boolalpha << solution.findNumberIn2DArray(matrix1, 5) << endl;  // 输出 true
    cout << boolalpha << solution.findNumberIn2DArray(matrix1, 20) << endl; // 输出 false

    // 测试用例2:空矩阵
    vector<vector<int>> matrix2;
    cout << boolalpha << solution.findNumberIn2DArray(matrix2, 1) << endl;  // 输出 false

    // 测试用例3:单行矩阵
    vector<vector<int>> matrix3 = { {1, 3, 5, 7} };
    cout << boolalpha << solution.findNumberIn2DArray(matrix3, 3) << endl;  // 输出 true

    return 0;
}

2.3 解析

示例矩阵(每行、每列递增)

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

查找目标值 14 的步骤解析

步骤 1:初始位置(右上角)

  • 指针位置:row=0,col=4(对应值 15
  • 比较:15 > 14 → 目标值不在当前列(第 4 列),向左移动一列(col=3
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

步骤 2:移动后位置

  • 指针位置:row=0,col=3(对应值 11
  • 比较:11 < 14 → 目标值不在当前行(第 0 行),向下移动一行(row=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

步骤 3:再次移动后位置

  • 指针位置:row=1,col=3(对应值 12
  • 比较:12 < 14 → 目标值不在当前行(第 1 行),向下移动一行(row=2
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

步骤 4:继续移动后位置

  • 指针位置:row=2,col=3(对应值 16
  • 比较:16 > 14 → 目标值不在当前列(第 3 列),向左移动一列(col=2
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:最终找到目标

  • 指针位置:row=2,col=2(对应值 9
  • 比较:9 < 14 → 目标值不在当前行(第 2 行),向下移动一行(row=3
  • 新位置:row=3,col=2(对应值 14),与目标值相等 → 返回true
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

示例矩阵(每行、每列递增)

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

查找目标值 14 的步骤解析

步骤 1:初始位置(右上角)

  • 指针位置:row=0,col=4(对应值 15
  • 比较:15 > 14 → 目标值不在当前列(第 4 列),向左移动一列(col=3
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

步骤 2:移动后位置

  • 指针位置:row=0,col=3(对应值 11
  • 比较:11 < 14 → 目标值不在当前行(第 0 行),向下移动一行(row=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

步骤 3:再次移动后位置

  • 指针位置:row=1,col=3(对应值 12
  • 比较:12 < 14 → 目标值不在当前行(第 1 行),向下移动一行(row=2
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

步骤 4:继续移动后位置

  • 指针位置:row=2,col=3(对应值 16
  • 比较:16 > 14 → 目标值不在当前列(第 3 列),向左移动一列(col=2
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:最终找到目标

  • 指针位置:row=2,col=2(对应值 9
  • 比较:9 < 14 → 目标值不在当前行(第 2 行),向下移动一行(row=3
  • 新位置:row=3,col=2(对应值 14),与目标值相等 → 返回true
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 步就找到目标值(而暴力解法可能需要遍历更多元素)。这种思路充分利用了数组的有序性,将查找效率从 O (n×m) 提升至 O (n+m)

三、考察点

核心考察点:

  1. 对数据结构特性的理解与利用
    题目明确给出 "每行递增、每列递增" 的特性,考察能否观察到这种有序性并加以利用,而不是简单粗暴地遍历所有元素。这是对 "问题分析能力" 和 "特性挖掘能力" 的考验。
  2. 算法优化意识
    暴力解法(双层循环)虽然能解决问题,但效率极低。题目更希望考察能否设计出时间复杂度更低的算法,体现 "优化思维"------ 即如何通过减少无效操作提升效率。
  3. 边界条件处理能力
    如空矩阵、单行 / 单列矩阵等特殊情况的处理,考察代码的健壮性。
  4. 逻辑思维的严谨性
    从右上角(或左下角)开始查找的逻辑是否清晰,能否准确设计出 "比较 - 移动" 的规则(何时左移、何时下移),避免逻辑漏洞。

优化算法相比暴力解法的核心优势:

维度 暴力解法(双层 for 循环) 优化算法(右上角查找)
时间效率 时间复杂度 O (n×m),需遍历所有元素 时间复杂度 O (n+m),最多遍历一行一列
资源消耗 大规模矩阵下运算量极大,耗时久 运算量随矩阵规模线性增长,效率显著提升
设计思想 无优化,仅依赖遍历完成功能 利用数据有序性,通过 "排除法" 减少无效操作

举例说明

对于一个 1000×1000 的矩阵:

  • 暴力解法最坏情况需要执行 1,000,000 次比较;
  • 优化算法最坏情况仅需 2000 次比较(1000 行 + 1000 列),效率提升 500 倍。

题目背后的考察目的:

这个问题本质上是为了区分 "能解决问题" 和 "能高效解决问题" 的思维方式:

  • 暴力解法体现的是 "完成功能" 的基础能力;
  • 优化解法则体现了 "理解本质、利用规律、追求效率" 的高阶思维,这也是编程和算法设计的核心素养。

在实际开发中,这种优化能力直接影响系统性能(尤其是处理大数据时),因此是企业面试中常见的基础考点。

相关推荐
独行soc12 分钟前
2025年渗透测试面试题总结-18(题目+回答)
android·python·科技·面试·职场和发展·渗透测试
艾伦~耶格尔41 分钟前
【数据结构进阶】
java·开发语言·数据结构·学习·面试
徐归阳3 小时前
第二十四天:虚函数与纯虚函数
c++
青草地溪水旁4 小时前
UML函数原型中constraint的含义,有啥用?
c++·uml
汤永红5 小时前
week1-[循环嵌套]画正方形
数据结构·c++·算法
重启的码农6 小时前
ggml 介绍(4) 计算图 (ggml_cgraph)
c++·人工智能
重启的码农6 小时前
ggml 介绍(5) GGUF 上下文 (gguf_context)
c++·人工智能·神经网络
悠哉清闲6 小时前
C++ #if
c++
Hard but lovely7 小时前
C++:stl-> list的模拟实现
开发语言·c++·stl·list