一、 预期效果
这就是我们最终要达到的效果,为了更便于理解整个过程,下面将分步进行处理。
二、规律分析
通过观察上图,我们很容易得出以下的规律:
> 每个数等于它上方两数之和(最上方的1除外)。
> 每行数字左右对称,由1开始逐渐变大。
> 第n行的数字有n+1项。
遗憾的是,我们目前无法通过坐标的方式告诉电脑这一规律,还是处于只可意会不可言传的阶段。例如,我们很清楚的知道,第二行的第二个元素2是怎么来的,但是我们在没有引入坐标之前无法准确的告诉电脑,同时,按照现在的排列方式,也很难描述列号。因为我们无法准确的判断在每行的第一个元素之前到底空了几个元素的位置。为了解决这个问题,我们可以对上述图形进行一定的改造。
三、图形改造
改造后的图形:
可以看到,每一行第一个元素前的空格都被消掉了。 这样可以很容易的通过行(i)和列(j)来表示每一个元素的坐标。从而通过规律打印出每一行的数据。在此基础上,输出时把空格补回来就达到想要的效果了。
2的坐标可以表示为(2,1),2是由坐标为(1,1)的元素加上坐标为(1,0)的元素得到。这里把这种规律称为直角规律,即图中的绿色直角。
但这时又出现了新的问题,边上的1怎么打印?
我们首先想到的可能是每一行先打印1,然后在每一行的末尾打印1。
这个方案需要我们对每一行的首尾单独处理,比较麻烦。
四、关键解决方案
那么我们有没有办法使边上的1也符合直角规律?答案是肯定的。请看下图:
在边上补上一堆0 ,此时,除了最上方的1,其余的1都符合直角规律,这样就做到了统一,从i = 1 开始,所有元素都符合直角规律——arr[ i ][ j ] = arr[ i - 1 ][ j ] + arr[i - 1][j - 1]。
这里就引入了二维数组,因为这种行与列的关系很容易让人联想到二维数组。
要创建几行几列的二维数组?这又是一个问题。
通过观察,我们发现上图的绿色三角形为等腰直角三角形。可以推出,在没补上0之前,行和列是相等的,在补充0之后,列 = 行 + 2 。
所以我们就可以创建这样一个二维数组——int arr[ROW][COL] = {0};ROW为行,COL为列,
其中COL = ROW + 2。以 ROW = 4 为例,该二维数组如下图:
//定义两个宏,方便修改
#define ROW 4
#define COL ROW+2
接着就是修改二维数组中的特定元素。如下图:
细节是保留了最左边的一列,从j=1开始修改。保留最左边的一列的原因是使边上的1也满足直角规律。修改完成后我们开始打印,打印也是从j=1开始的,因为这些0不是我们想看到的,它们只起到了辅助作用。当然,这里的打印并未加上该有的空格,只是检验一下上述做法是否达到了我们的阶段性目标。 下面给出部分代码及其运行结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//定义两个宏,方便修改
#define ROW 4
#define COL ROW+2
int main()
{
int arr[ROW][COL] = { 0 };//创建二维数组
int i = 0;//行号
int j = 0;//列号
arr[0][1] = 1;//单独处理最上方的1
for (i = 1; i < ROW; i++)//注意,因为已经单独处理了最上方的1,所以i从1开始
{
for (j = 1; j < COL - 1; j++)//j从1开始,保留最左边一列0
{
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];//直角规律
}
}
//打印
for (i = 0; i < ROW; i++)//i从0开始可以把最上方的1也打印出来
{
for (j = 1; j <= i + 1; j++)
{
printf("%4d", arr[i][j]);
}
printf("\n");//打印完一行后换行
}
return 0;
}
进行到这,就差把空格补上了。 下面将谈谈如何把空格补上。
五、打印空格
//打印
for (i = 0; i < ROW; i++)//i从0开始可以把最上方的1也打印出来
{
//我们需要在打印每行的元素前打印空格
//打印空格
for (j = 1; j <= i + 1; j++)
{
printf("%4d", arr[i][j]);
}
printf("\n");//打印完一行后换行
}
我们只需要在上述代码里头加上一个for循环即可。下面 我们来分析如何打印空格。
printf("%4d", arr[i][j]);//占用4个宽度
第一行:空格数为 (ROW / 2 ) * 4
第二行:空格数为 ((ROW-1)/ 2 ) * 4
第三行:空格数为((ROW- 2)/ 2)* 4
以此类推……
化简得到:
ROW * 2
(ROW - 1)* 2
(ROW - 2 )* 2
……
这里有个小细节值得注意,最好选择偶数宽度,这样一来可以和2抵消,ROW-1是奇数时就不用担心结果取整了。
补充打印空格的代码
//打印
for (i = 0; i < ROW; i++)//i从0开始可以把最上方的1也打印出来
{
for (j = i; j < ROW; j++)//后边单独解释
{
printf(" ");//两个空格
}
for (j = 1; j <= i + 1; j++)
{
printf("%4d", arr[i][j]);
}
printf("\n");//打印完一行后换行
}
对下面代码的解释:
for (j = i; j < ROW; j++)
{
printf(" ");//两个空格
}
i = 0 时,j = 0, 循环ROW次,每次打印两个空格,共打印ROW * 2 个空格。
i = 1 时,j = 1,循环ROW-1次,每次打印两个空格,共打印(ROW - 1)* 2个空格
符合上文分析得出的结论。
六、完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//定义两个宏,方便修改
#define ROW 10
#define COL ROW+2
int main()
{
int arr[ROW][COL] = { 0 };//创建二维数组
int i = 0;//行号
int j = 0;//列号
arr[0][1] = 1;//单独处理最上方的1
for (i = 1; i < ROW; i++)//注意,因为已经单独处理了最上方的1,所以i从1开始
{
for (j = 1; j < COL - 1; j++)//j从1开始,保留最左边一列0
{
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];//直角规律
}
}
//打印
for (i = 0; i < ROW; i++)//i从0开始可以把最上方的1也打印出来
{
for (j = i; j < ROW; j++)//后边单独解释
{
printf(" ");//两个空格
}
for (j = 1; j <= i + 1; j++)
{
printf("%4d", arr[i][j]);
}
printf("\n");//打印完一行后换行
}
return 0;
}