矩阵边界遍历:顺时针与图案打印的两种高效解法
矩阵的边界遍历是算法面试中的经典问题,今天我们来拆解两个相关场景:顺时针提取边界元素 和 保留原位置的边界图案打印。
场景一:顺时针提取边界元素
给定一个 n × m 的矩阵,要求从左上角开始,按顺时针方向依次输出边界上的所有元素。
示例 1:
输入:
1 2 3 4
5 6 7 8
9 10 11 12
输出:[1, 2, 3, 4, 8, 12, 11, 10, 9, 5]
示例 2:
输入:
1 2
3 4
输出:[1, 2, 4, 3]
学矩阵遍历时,总觉得边界处理很绕?想彻底搞懂顺时针打印和图案生成的底层逻辑,光看静态代码可不够。强烈推荐一个宝藏网站------图码,它把"算法可视化"做到了极致。不仅有60多种数据结构与算法的交互式动画,还能上传自己的C++/Java/Python代码,让每一步执行都"动"起来。无论是备战408考研,还是突击数据结构期末考试,用它来拆解复杂逻辑,效率直接翻倍。现在就去体验,保证让你相见恨晚。
图码-数据结构与算法交互式可视化平台
访问网站:https://totuma.cn
解法思路:四次遍历法
核心思想是将边界拆解为四个方向的分段遍历:
- 第一行(从左到右)
- 最后一列(从上到下,跳过第一行)
- 最后一行(从右到左,跳过最后一个)
- 第一列(从下到上,跳过第一行和最后一行)
图示:
迭代1 - 顶行从左到右:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9,10,11,12]
迭代2 - 右列从上到下:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9,10,11,12]
迭代3 - 底行从右到左:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9,10,11,12]
迭代4 - 左列从下到上:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9,10,11,12]
代码实现(Python):
python
def boundaryTraversal(mat):
n = len(mat)
m = len(mat[0])
res = []
# 顶行
for j in range(m):
res.append(mat[0][j])
# 右列
for i in range(1, n):
res.append(mat[i][m-1])
# 底行
for j in range(m-2, -1, -1):
res.append(mat[n-1][j])
# 左列
for i in range(n-2, 0, -1):
res.append(mat[i][0])
return res
复杂度分析:
- 时间复杂度:O(n + m)
- 空间复杂度:O(1)(不计输出数组)
注意:当矩阵只有一行或一列时,需避免重复遍历,代码中应加入边界条件检查。
场景二:保留原位置的边界图案打印
给定一个矩阵,要求按原样输出矩阵,但只显示边界上的元素,内部元素用空格代替。
示例:
输入:
1 2 3 4
5 6 7 8
1 2 3 4
5 6 7 8
输出:
1 2 3 4
5 8
1 4
5 6 7 8
解法思路:双循环条件判断
遍历每个元素,如果是边界(i==0、j==0、i==m-1、j==n-1)则打印元素,否则打印空格。
代码实现(Python):
python
def print_boundary(a):
m, n = len(a), len(a[0])
for i in range(m):
for j in range(n):
if i == 0 or j == 0 or i == m-1 or j == n-1:
print(a[i][j], end=' ')
else:
print(' ', end='')
print()
复杂度分析:
- 时间复杂度:O(n × m)
- 空间复杂度:O(1)
总结
| 场景 | 方法 | 时间复杂度 | 适用场景 |
|---|---|---|---|
| 顺时针提取边界 | 四次遍历 | O(n+m) | 需要一维序列结果 |
| 保留原位置打印 | 条件判断 | O(n×m) | 需要可视化矩阵边界 |
掌握这两种思路,矩阵边界相关的问题基本都能迎刃而解。面试中记得考虑单行/单列的边界情况,避免重复遍历哦!
参考实现(C++/Java/JavaScript 等):
cpp
// C++ 版本 - 顺时针遍历
#include <iostream>
#include <vector>
using namespace std;
vector<int> boundaryTraversal(vector<vector<int>>& mat) {
int n = mat.size(), m = mat[0].size();
vector<int> res;
// 顶行
for (int j = 0; j < m; j++) res.push_back(mat[0][j]);
// 右列
for (int i = 1; i < n; i++) res.push_back(mat[i][m-1]);
// 底行
for (int j = m-2; j >= 0; j--) res.push_back(mat[n-1][j]);
// 左列
for (int i = n-2; i > 0; i--) res.push_back(mat[i][0]);
return res;
}
java
// Java 版本 - 图案打印
public static void printBoundary(int[][] a) {
int m = a.length, n = a[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 || j == 0 || i == m-1 || j == n-1)
System.out.print(a[i][j] + " ");
else
System.out.print(" ");
}
System.out.println();
}
}
javascript
// JavaScript 版本 - 顺时针遍历
function boundaryTraversal(mat) {
let n = mat.length, m = mat[0].length;
let res = [];
for (let j = 0; j < m; j++) res.push(mat[0][j]);
for (let i = 1; i < n; i++) res.push(mat[i][m-1]);
for (let j = m-2; j >= 0; j--) res.push(mat[n-1][j]);
for (let i = n-2; i > 0; i--) res.push(mat[i][0]);
return res;
}
输出示例:
1 2 3 4 8 12 11 10 9 5
希望这篇笔记对你有帮助!如果你有其他矩阵遍历的奇技淫巧,欢迎在评论区分享~