文章目录
一、题目
在一个 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)
三、考察点
核心考察点:
- 对数据结构特性的理解与利用
题目明确给出 "每行递增、每列递增" 的特性,考察能否观察到这种有序性并加以利用,而不是简单粗暴地遍历所有元素。这是对 "问题分析能力" 和 "特性挖掘能力" 的考验。 - 算法优化意识
暴力解法(双层循环)虽然能解决问题,但效率极低。题目更希望考察能否设计出时间复杂度更低的算法,体现 "优化思维"------ 即如何通过减少无效操作提升效率。 - 边界条件处理能力
如空矩阵、单行 / 单列矩阵等特殊情况的处理,考察代码的健壮性。 - 逻辑思维的严谨性
从右上角(或左下角)开始查找的逻辑是否清晰,能否准确设计出 "比较 - 移动" 的规则(何时左移、何时下移),避免逻辑漏洞。
优化算法相比暴力解法的核心优势:
维度 | 暴力解法(双层 for 循环) | 优化算法(右上角查找) |
---|---|---|
时间效率 | 时间复杂度 O (n×m),需遍历所有元素 | 时间复杂度 O (n+m),最多遍历一行一列 |
资源消耗 | 大规模矩阵下运算量极大,耗时久 | 运算量随矩阵规模线性增长,效率显著提升 |
设计思想 | 无优化,仅依赖遍历完成功能 | 利用数据有序性,通过 "排除法" 减少无效操作 |
举例说明 :
对于一个 1000×1000 的矩阵:
- 暴力解法最坏情况需要执行 1,000,000 次比较;
- 优化算法最坏情况仅需 2000 次比较(1000 行 + 1000 列),效率提升 500 倍。
题目背后的考察目的:
这个问题本质上是为了区分 "能解决问题" 和 "能高效解决问题" 的思维方式:
- 暴力解法体现的是 "完成功能" 的基础能力;
- 优化解法则体现了 "理解本质、利用规律、追求效率" 的高阶思维,这也是编程和算法设计的核心素养。
在实际开发中,这种优化能力直接影响系统性能(尤其是处理大数据时),因此是企业面试中常见的基础考点。