我在这里找到了这个链接:如何遍历矩阵的对角线条带
#include <stdio.h>
int main()
{
int x[3][4] = { 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12};
int m = 3;
int n = 4;
for (int slice = 0; slice < m + n - 1; ++slice) {
printf("Slice %d: ", slice);
int z1 = slice < n ? 0 : slice - n + 1;
int z2 = slice < m ? 0 : slice - m + 1;
for (int j = slice - z2; j >= z1; --j) {
printf("%d ", x[j][slice - j]);
}
printf("\n");
}
return 0;
}
输出:
Slice 0: 1
Slice 1: 5 2
Slice 2: 9 6 3
Slice 3: 10 7 4
Slice 4: 11 8
Slice 5: 12
我发现这种方法非常优雅,因为它只需要两个附加变量(z1和z2)的内存,这些变量基本上保存有关每个片段长度的信息。外循环移动到切片编号(slice),然后内循环通过索引移动到每个切片:
slice - z1 - z2
。然后您需要的所有其他信息是算法从哪里开始以及如何在矩阵中移动。在前面的示例中,它将首先向下移动矩阵,达到底部之后,将向右移动:(0,0) -> (1,0) -> (2,0) -> (2,1) -> (2,2) -> (2,3)。同样,此模式由变量z1和z2捕获。行增量与
slice
号一起递增,直到达到底部,然后
z2
将开始增加,可以用于使行索引保持在其位置常数:
slice-z2
。每个切片的长度可由以下公式得知:
slice-z1-z2
,执行以下操作:
(slice - z2) - (slice - z1 -z2)
(减法是因为算法按升序m--,n ++移动),结果为
z1
,这是内部循环的停止准则。只剩下列索引,这个索引方便地从j在到达底部后保持不变而继承,之后开始递增。
前面的算法仅按从左到右的升序移动,从左上角(0,0)开始。当我需要此算法时,我还需要按降序从矩阵底部左侧(m,n)开始搜索矩阵。因为我对算法非常着迷,所以决定深入了解并进行修改:
- 切片长度再次由以下公式得知:
slice-z1-z2
- 片段的起始位置是:(2,0) -> (1,0) -> (0,0) -> (0,1) -> (0,2) -> (0,3)
- 每个片段的移动是m ++和n ++
我发现将其描述如下很有用:
- slice=0 z1=0 z2=0 (2,0) (列索引= 行索引 - 2)
- slice=1 z1=0 z2=0 (1,0) (2,1) (列索引= 行索引 - 1)
- slice=2 z1=0 z2=0 (0,0) (1,1) (2,2) (列索引= 行索引 + 0)
- slice=3 z1=0 z2=1 (0,1) (1,2) (2,3) (列索引= 行索引 + 1)
- slice=4 z1=1 z2=2 (0,2) (1,3) (列索引= 行索引 + 2)
- slice=5 z1=2 z2=3 (0,3) (列索引= 行索引 + 3)
推导如下: j = (m-1) - slice + z2
(使用slice长度的表达式作为停止条件:((m-1) - slice + z2)+(slice -z2 - z1)
)结果为:(m-1) - z1
现在我们有了内部循环的参数:for (int j = (m-1) - slice + z2; j < (m-1) - z1; j++)
行索引由j确定,我们知道当j开始保持不变时,列索引才开始递增,因此再次在表达式中加入j并不是个坏主意。通过上面总结的求和结果之间的差异,我注意到差异总是等于j - (slice - m +1)
,将其用于一些其他情况的测试后,我相信它适用于所有情况(我不是数学家;P),因此从左下方开始下降运动的算法如下:
#include <stdio.h>
int main()
{
int x[3][4] = { 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12};
int m = 3;
int n = 4;
for (int slice = 0; slice < m + n - 1; ++slice) {
printf("Slice %d: ", slice);
int z1 = slice < n ? 0 : slice - n + 1;
int z2 = slice < m ? 0 : slice - m + 1;
for (int j = (m-1) - slice + z2; j <= (m-1) - z1; j++) {
printf("%d ", x[j][j+(slice-m+1)]);
}
printf("\n");
}
return 0;
}
现在我把另外两个方向留给你(仅当顺序真正重要时才重要)。这个算法非常具有挑战性,即使你认为自己知道它是如何工作的,它仍然会让你措手不及。但我认为它非常美丽,因为它确实像你所期望的那样在矩阵中移动。如果有人了解更多关于该算法的信息,例如名称,那么我可以看看我在这里做的事情是否合理,或许有更好的解决方案。