关于二维数组的小技巧

时间:2023-01-23 23:47:14

常常遇到这样的问题,求二维数组周边元素之和,求二维数组上三角元素之和等等,又或者会遇到这样的问题,构造一个二维数组让它的最外层是1,第二层是2……

在此笔者简单介绍几种本人遇到过的比较简略的算法,与大家分享之。

首先容易知道,一个二维数组a[m][n]实际就是m×n矩阵,那么诸多问题都可以从线性代数的角度去理解并加以解决。另一方面,矩阵的对称性也给了我们很多的思路。

Q1:求一个二维数组a[M][N]的周边元素之和

首先观察一个二维数组a[3][3]每个元素的下标(见表格1),容易观察到周边元素或者有0或者有2。那么是不是对N×N也有类似的结论呢?答案是肯定的。考虑N×N情况,第一行一定是行地址为0,第一列一定是列地址为0,而最后一行行地址则是N-1,最后一列列地址也是N-1,至此,我们已经可以找到解决表达周边元素的方法了。

因为周边元素必有一个地址是0或者N-1,同时也可以知道其他元素都不具备这样的特点(把周边元素全部遮挡起来,就变成一个N-1×N-1矩阵,此时的周边元素显然是原矩阵的1和N-2)

那么可以采用遍历的方法查找所有数组元素,再通过if语句去判断是不是具备周边元素的特点。

代码如下:

char a[N][N];
int i,j,sum;
for(i=0;i<N;i++)
{for(j-0;j<N;j++)

{if(i==0||j==0||i==N-1||j==N-1)
sum+=a[i][j];
}
}

而对于M×N矩阵,同理可得。

另一种想法则是用“减法”,遍历N×N矩阵的全部元素和,再减去N-1×N-1矩阵的元素和,得到的就是周边元素的,但是显然没有上面的方法好,故略去不谈。

 

表格1
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1

2,2

Q2:求一个二维数组a[N][N]上三角元素之和

上三角矩阵来源于线性代数,在liner algebra中有着广泛的应用,比如用来判断一个解空间的维数亦或对矩阵作LU分解。

实际上三角矩阵和下三角矩阵有着高度的对称性,所以无论从哪条路出发,都是很容易得到另一部分的。关键是观察到其规律所在,a[i][N]总是有N-i个元素被算在内,而这样的一个循环是很容易设计的。

for(int i=0;i<N;i++)
{
  for(int j=0;j<=i;j++)

{printf("%d“,a[i][j]);

}      
}

这样的一个循环就实现了输出下三角元素,而只需将j作改动便成了上三角

for(int i=0;i<N;i++)
{
  for(int j=N-1-i;j>=i;j--)

{printf("%d“,a[i][j]);

}      
}

只要找到了上三角元素的输出方法,显然与上三角/下三角有关的问题,都迎刃而解了。

Q3:构造一个二维数组,最外层是1,第二层是2,以此类推。

这是一道比较麻烦的题。一种想法是在Q1中被我们舍弃的思路,做“减法”。

构造一个N×N数组并给所有元素赋值1,然后依次加,来实现目的。

int a[N][N]={0};
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
a[i][j]=1;//这一步实现给所有元素赋值
}
}
for(int x1=1;x1<N-1;x1++)
{for(int x2=1;x2<N-1;x2++)
{a[x1][x2]+=1;
}
}
for(int x3=2;x3<N-2;x3++)
{for(int x4=2;x4<N-2;x4++)
{a[x3][x4]+=1;
}
}

上述代码只是一小部分,读者应该能体会到其中的思想所在。现在来算一笔账,首先我们要根据N来算出数组里的最大数(N+1)/2,其次算出它所在的行列a[(N-1)/2][(N-1)/2],然后才能去写下上述代码,而且当N很大的时候,增加的工作量也是相当大的。at the same time,它也并不是和最理想的情况:我们scanf一个N,就能给出一个这样的数组。

那么我们来参考一下标准答案:

int a[N][N]={0};
 int i,j,k,m,n=N;
  if(n%2 == 0) m=n/2;
  else m = n/2+1;
  for(i=0;i<m;i++)
  { for( j =i;j<n-i;j++)
      a[i][j]=a[ n-i-1][j]=i+1;
    for( k =i+1; k<n-i; k++)
      a[k][i]=a[k][n-i-1]=i+1;

首先设出数组a[N][N],然后来判定N是一个偶数还是一个奇数(这时候我们还看不出来这样的目的)。然后作了一个for循环,我们依次执行来看看:

a[0][0]=a[n-1][0]=1一直到a[0][n-1]=a[n-1][n-1]=1;

a[1][0]=a[1][n-1]=1一直到a[n-1][0]=a[n-1][n-1]=1;实现了最外围的行列赋值

原来如此,因为每一圈都只是两行两列,那么就直接找这两行两列与N,i的关系来处理

而m的处理也十分巧妙,到最后可以发现偶数正好是最里面一圈的四个,而奇数则正好最后都归为那个最大数(N+1)/2


 

这样的技巧性在很多算法中都有体现,但最后实际仍然是人的思维,我不愿强调去多做题,但是做一些题目确实能够让你打开眼界,见识许许多多不一样的算法。

记得以前看到过一个anecdote:

面试官让考生设计一个算法去判断一个数是否为素数,大部分都是选择遍历小于k的全部数来判断,只有极少数人遍历小于k/2,而选择√k的则是更少数。

从中我们可以领略一二做题的必要性。