第3章第1节练习题2 回形矩阵

时间:2023-02-22 22:07:42

问题描述

回型矩阵即使用二维数组完成来绕圈圈似的赋值,举例说明如下所示的形式即为回型数组。

第3章第1节练习题2 回形矩阵

算法思想

就单纯的在二维数组中按照某种顺序输出连续的数字而言,实际上是玩弄数组下标游戏。因此将回型数组写成下标所表示的形式,如下图所示,其中00的意思是数组下标(0,0),后面的以此类推。

第3章第1节练习题2 回形矩阵

当得到上述所示的下标所列出来的图形时,可以发现回型数组主要由的一个轮回刚好是矩阵的四条边,而第一圈的最后一条竖直的边并不与第一圈的开始重合,因此为了方便处理,可以将每条边的“最后一个元素”单独处理,然后画出其主对角线和副对角线,可以得到下图,其中绿色的两条斜线分别为主对角线和副对角线。

第3章第1节练习题2 回形矩阵

从图中可以看到:

  • 数字从1~4的过程中,数组下标从00~03;即行下标保持不变,而列下标保持递增
  • 数字从5~8的过程中,数组下标从04~34;即行下标保持递增,而列下标保持不变
  • 数字从9~12的过程中,数组下标从44~41;即行下标保持不变,而列下标保持递减
  • 数字从13~16的过程中,数组下标从40~10;即行下标保持递减,而列下标保持不变

上述过程完成了一个轮回,其他的与上面的步骤相似。对上述步骤分析结合上图,可以得到
水平方向:开始于主对角线,结束于副对角线;
竖直方向:开始于副对角线,结束于主对角线;

对于主对角线上的元素下标满足行下标等于列下标
对于副对角线上的元素行下标与列下标之和等于定常数,而这个定常数恰好为矩阵的维数减1

由此可以得到主对角线元素下标为:(i,i);
副对角线下标为:(i,N-1-i)或(N-1-i,i);

那么再分析下上述的轮回,便可以得到通项表达式:

  • 数字从1~4的过程中,数组下标从(i,i)->(i,N-2-i);
  • 数字从5~8的过程中,数组下标从(i,N-1-i)->(i-1,i);
  • 数字从9~12的过程中,数组下标从(i,i)->(i,N-2-i);
  • 数字从13~16的过程中,数组下标从(N-1-i,i)->(i-1,i);
    注:上述的i只是一种表示方式,不同行列数字变化的过程中,i并不相同。

这里应该注意到该矩阵的维数是奇数还是偶数。如果是奇数,应该注意到最里面的那个数是需要开启新的一轮轮回的,故应特殊对待;而对于偶数,最后一次轮回就可以完成所有的赋值过程。

综上所述,算法的描述如下。

算法描述

void ClipArray(int A[][N]){
int cnt=0;
for(int i=0;i<N/2;i++){
//从左向右
for(int j=i;j<N-1-i;j++){
A[i][j]=++cnt;
}
//从上向下
for(int j=i;j<N-1-i;j++){
A[j][N-1-i]=++cnt;
}
//从右向左
for(int j=N-1-i;j>i;j--){
A[N-1-i][j]=++cnt;
}
//从下向上
for(int j=N-1-i;j>i;j--){
A[j][i]=++cnt;
}
}
if(N%2!=0){
A[N/2][N/2]=++cnt;
}
}

具体代码见附件。


附件

#include<stdio.h>
#define N 4

void ClipArray(int (*)[N]);
void Show(int (*)[N]);

int main(int argc,char* argv[]){
int Arry[N][N]={{0}};
ClipArray(Arry);
Show(Arry);
return 0;
}

void ClipArray(int A[][N]){
int cnt=0;
for(int i=0;i<N/2;i++){
//从左向右
for(int j=i;j<N-1-i;j++){
A[i][j]=++cnt;
}
//从上向下
for(int j=i;j<N-1-i;j++){
A[j][N-1-i]=++cnt;
}
//从右向左
for(int j=N-1-i;j>i;j--){
A[N-1-i][j]=++cnt;
}
//从下向上
for(int j=N-1-i;j>i;j--){
A[j][i]=++cnt;
}
}
if(N%2!=0){
A[N/2][N/2]=++cnt;
}
}

void Show(int A[][N]){
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
printf("%3d",A[i][j]);
if(j==N-1){
printf("\n");
}
}
}
}