1. 题目解析与核心思路
这道来自LeetCode Hot 100的第21题"搜索二维矩阵II"是一个经典的算法问题。题目给定一个m×n的二维矩阵,其中每行从左到右升序排列,每列从上到下升序排列。要求设计一个高效的算法来搜索目标值target。
1.1 矩阵特性分析
这个矩阵的特殊之处在于:
- 每行元素从左到右递增
- 每列元素从上到下递增
- 但不像普通有序矩阵那样严格(即第i行的最后一个元素不一定小于第i+1行的第一个元素)
这种结构被称为"Young tableau",在计算机科学中有重要应用。理解这种结构特性是解题的关键。
1.2 暴力解法与优化方向
最直观的解法是暴力搜索,时间复杂度O(mn)。但题目要求更高效的解法,我们需要利用矩阵的有序特性。
观察发现:
- 矩阵左上角是最小值,右下角是最大值
- 从任意位置出发,向右和向下都是递增方向
- 这种结构允许我们进行"剪枝"操作
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高效算法实现
2.1 对角线搜索法
一个直观的思路是从矩阵的某个角落开始搜索。经过分析,选择右上角或左下角最为合适。
以右上角为例:
- 初始化指针在右上角(matrix[0][n-1])
- 比较当前元素与target:
- 如果等于,返回true
- 如果大于,排除当前列(因为该列所有元素都更大),向左移动
- 如果小于,排除当前行(因为该行所有元素都更小),向下移动
- 重复直到找到目标或越界
python复制def searchMatrix(matrix, target):
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
row, col = 0, n - 1
while row < m and col >= 0:
if matrix[row][col] == target:
return True
elif matrix[row][col] > target:
col -= 1
else:
row += 1
return False
时间复杂度:O(m+n),因为每次迭代都排除一行或一列
空间复杂度:O(1)
2.2 二分搜索优化
虽然对角线搜索已经很高效,但我们还可以进一步优化。可以对每行进行二分搜索:
python复制def searchMatrix(matrix, target):
for row in matrix:
left, right = 0, len(row) - 1
while left <= right:
mid = (left + right) // 2
if row[mid] == target:
return True
elif row[mid] < target:
left = mid + 1
else:
right = mid - 1
return False
时间复杂度:O(mlogn)
空间复杂度:O(1)
这种方法在行数m较小,列数n较大时表现更好。
3. 算法比较与选择
3.1 性能对比
| 算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力搜索 | O(mn) | O(1) | 不推荐 |
| 对角线搜索 | O(m+n) | O(1) | 通用 |
| 二分搜索 | O(mlogn) | O(1) | m小n大 |
3.2 选择建议
- 一般情况下优先选择对角线搜索法,因为其时间复杂度稳定
- 当矩阵非常"扁平"(m远小于n)时,二分搜索法可能更优
- 实际应用中可以根据矩阵形状动态选择算法
4. 边界条件与注意事项
4.1 特殊输入处理
- 空矩阵检查:
python复制if not matrix or not matrix[0]: return False - 单行或单列矩阵
- 矩阵元素重复的情况
4.2 常见错误
- 初始位置选择错误(应选择右上角或左下角)
- 移动方向错误(大于target时应向左,小于时应向下)
- 循环终止条件错误(注意行列的边界)
4.3 调试技巧
- 打印每次迭代的位置和值
- 对小矩阵手动模拟算法流程
- 测试极端情况(如目标是最小或最大值)
5. 实际应用场景
这种搜索算法在以下场景有实际应用:
- 数据库索引查询
- 图像处理中的区域搜索
- 游戏开发中的地图寻路
- 金融数据分析
6. 算法扩展与变种
6.1 统计出现次数
可以修改算法统计target出现的次数:
python复制def countInMatrix(matrix, target):
if not matrix or not matrix[0]:
return 0
m, n = len(matrix), len(matrix[0])
row, col = 0, n - 1
count = 0
while row < m and col >= 0:
if matrix[row][col] == target:
count += 1
# 继续搜索其他可能位置
row += 1
col -= 1
elif matrix[row][col] > target:
col -= 1
else:
row += 1
return count
6.2 查找第k小元素
利用这种矩阵结构还可以高效查找第k小的元素,这需要结合堆(heap)的使用。
7. 性能优化实践
在实际编码面试中,可以这样展示优化思路:
- 先提出暴力解法
- 分析矩阵特性
- 提出对角线搜索法
- 讨论可能的优化(如二分搜索)
- 分析时间/空间复杂度
- 处理边界条件
这种逐步优化的思路能很好展示问题解决能力。
8. 不同语言实现
8.1 Java实现
java复制public boolean searchMatrix(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int row = 0, col = matrix[0].length - 1;
while (row < matrix.length && col >= 0) {
if (matrix[row][col] == target) {
return true;
} else if (matrix[row][col] > target) {
col--;
} else {
row++;
}
}
return false;
}
8.2 C++实现
cpp复制bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
int row = 0, col = matrix[0].size() - 1;
while (row < matrix.size() && col >= 0) {
if (matrix[row][col] == target) {
return true;
} else if (matrix[row][col] > target) {
col--;
} else {
row++;
}
}
return false;
}
9. 单元测试用例
完整的测试应该包含以下情况:
- 常规测试
python复制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] ] assert searchMatrix(matrix, 5) == True assert searchMatrix(matrix, 20) == False - 边界测试
- 查找第一个元素
- 查找最后一个元素
- 查找不存在的较小值
- 查找不存在的较大值
- 特殊矩阵测试
- 单行矩阵
- 单列矩阵
- 空矩阵
10. 常见面试问题
在面试中可能会被问到:
- 为什么选择从右上角开始搜索?
- 这个算法的时间复杂度是如何推导的?
- 如果矩阵有重复元素怎么办?
- 如何修改算法来返回所有匹配的位置?
- 这个算法相比二分搜索有什么优势?
准备这些问题可以帮助你在面试中更好地展示思考过程。
