1. 概述
在本文中,我们将学习一种遍历矩阵元素的算法,该算法按照正方形螺旋(Square Spiral)的顺序访问矩阵中的每个元素。这种遍历方式常用于图像处理、游戏地图扫描、数据压缩等场景。
2. 什么是螺旋遍历?
螺旋遍历指的是从矩阵的一个点出发,按顺时针或逆时针方向,像画圈一样逐步访问矩阵中的每个元素。与数学上的连续螺旋曲线不同,矩阵螺旋是离散且逐步推进的。
比如下面是一个 3x3 矩阵的顺时针螺旋遍历顺序:
1 2 3
8 9 4
7 6 5
也可以从中心向外扩展,形成反向螺旋:
9 2 3
8 1 4
7 6 5
3. 非方阵的处理
当矩阵不是方阵时(行数 ≠ 列数),我们依然可以采用类似的螺旋逻辑进行遍历。通常做法是:
- 将原矩阵“扩展”为一个方阵(大小为 max(N, M))
- 遍历整个方阵,跳过那些超出原矩阵范围的元素
例如一个 3x5 的矩阵,扩展后可按如下顺序遍历:
15 9 2 3 10
14 8 1 4 11
13 7 6 5 12
4. 从中心向外螺旋遍历
我们以矩阵中心为起点,向外螺旋遍历至边界。为了实现这个过程,我们需要维护:
- 当前坐标
(x, y)
,相对于中心点 - 移动方向
(dx, dy)
,每次在拐角处旋转方向
✅ 顺时针螺旋算法
public static void spiralClockwise(int N, int M, int[][] A) {
int x = 0, y = 0;
int dx = 0, dy = -1;
for (int i = 0; i < Math.pow(Math.max(N, M), 2); i++) {
// 判断当前坐标是否在原矩阵范围内
if (-N / 2 < x && x <= N / 2 && -M / 2 < y && y <= M / 2) {
// 访问 A[x][y],这里可以替换成你的处理逻辑
System.out.print(A[x + N / 2][y + M / 2] + " ");
}
// 判断是否需要转向
if (x == y || (x == -y && x < 0) || (x == 1 - y && x > 0)) {
// 旋转方向
int temp = dx;
dx = -dy;
dy = temp;
}
x += dx;
y += dy;
}
}
✅ 逆时针螺旋算法
public static void spiralCounterClockwise(int N, int M, int[][] A) {
int x = 0, y = 0;
int dx = 0, dy = 1;
for (int i = 0; i < Math.pow(Math.max(N, M), 2); i++) {
if (-N / 2 < x && x <= N / 2 && -M / 2 < y && y <= M / 2) {
System.out.print(A[x + N / 2][y + M / 2] + " ");
}
if (x == -y || (x == y && x > 0) || (x == -1 + y && x < 0)) {
// 旋转方向
int temp = dx;
dx = dy;
dy = -temp;
}
x += dx;
y += dy;
}
}
⚠️ 注意:在访问 A[x][y] 时,需要根据矩阵的实际索引进行偏移调整,比如加上 N/2 和 M/2。
5. 从角点向中心螺旋遍历
如果我们希望从角点开始向中心螺旋遍历,只需要将从中心向外遍历的路径反转即可。
例如,对一个 5x7 的矩阵,从中心向外遍历得到的顺序是:
1 2 3 ...
反转后,从角点向中心遍历的顺序是:
... 3 2 1
这种做法在需要“由外向内”扫描的场景中非常有用,比如图像滤波、地图寻路等。
6. 总结
在本文中,我们介绍了如何按螺旋顺序访问矩阵中的元素,包括:
- 什么是矩阵的螺旋遍历
- 如何处理非方阵
- 从中心向外的顺时针和逆时针螺旋算法
- 从角点向中心的反向螺旋实现方式
这些算法在图像处理、AI路径规划、图形渲染等领域都有广泛应用。掌握其实现原理,有助于你更好地应对复杂的数据结构问题。