学过C语言编程的,势必都要接触到杨辉三角,它本身并不深奥,就是一种二项式系数的几何排列,然后以三角形的形式给我们展现出来,说白了,就是两个未知数之和的幂次方后的各项系数。
以下是我放在github上的代码链接,需要的可自行下载:
https://github.com/zhengzebin525/basic_algorithm
有图有真相,杨辉三角的几何排列如下:
稍微解释一下,第一行就是(a+b)的0次方的各项系数,第二行就是(a+b)的1次方的各项系数,第三行就是(a+b)的二次方的各项系数,依次类推,正是因为有了杨辉三角,所以我们在计算幂次方各项系数的时候可以完全按照杨辉三角排列来匹配各项的系数,大大提高了我们的计算效率。
而且也可以轻易看出杨辉三角的基本特征:每一行两边的系数都是1,其他各项的系数都是肩上(上一行)两个系数之和。
既然是跟编程有关,接下来我们就讲讲两种用C语言构建杨辉三角的方法。
方法一:利用数组和循环构建杨辉三角;
方法二:利用数据结构中的队列构建杨辉三角;
方法一就稍微简单了,只要有最基础的C语言编程功底,按照杨辉三角的基本特征就可以将算法揣摩出来,代码如下:
#include<stdio.h>
#define N 20 //限制最大行数
int main()
{
int i,j,n,k,triangle[N][N];
printf("请输入要打印的行数,最大不超过20行:");
scanf("%d",&n);
while(n>20 | n < 0)
{
printf("超过最大行数,请重新输入:");
scanf("%d",&n);
}
for(i=0;i<n;i++)
{
triangle[i][0] = triangle[i][i] = 1; //杨辉三角每一行两端的系数都是1
}
for(i=2;i<n;i++)
{
for(j=0;j<i-1;j++)
{
//其他各项的系数都是左上方和右上方两个系数之和
triangle[i][j+1] = triangle[i-1][j] + triangle[i-1][j+1];
}
}
for(i=0;i<n;i++)
{
for(k=n-i-1;k>0;k--)
{
printf(" "); //为了打印出来好看,就需要根据每一行打印不同的空格
}
for(j=0;j<=i;j++)
{
printf("%5d",triangle[i][j]);
}
printf("\n");
}
return 0;
}
第二种方法,就需要扎实的编程基本功,同时还要对数据结构有一定的熟练度,在这里我就讲讲如何利用队列来构建杨辉三角,首先要知道队列具体是个什么东西。
学过链表的都知道,链表可以在任何位置进行插入和删除,那么当链表被进行限制,只能在同一端进行插入和删除,那么这个被限制的链表,就成了栈;同样的,当链表被限制只能在两端中的一端进行插入,另一端进行删除,那么这个被限制的链表就是队列,插入的那一端就是队尾,删除的那一端就是队首。
假设我们此时需要打印出四行的杨辉三角,以下是算法的设计思路:
因为杨辉三角都是从第一行开始构建的,因为第一行只有一个系数1,所以队列先插入系数1,此时还没有开始打印,因此打印区是空的。
然后是开始插入第二行的系数数据,从第二行开始,遵循四个步骤:
1、第一个系数肯定是去,因此队列插入1;
2、进行出队,把出队数据打印出来,并将出队元素与此时队首元素进行相加并保存,保存的数据就是中间各项系数了;
3、每一行最后一个系数肯定是1,因此队列插入一个1;
4、将此时处于队首的元素出队,并打印出来;
第二行的第一个系数毫无疑问肯定是1,所以队列再插入一个1,中间没有任何需要肩上系数相加的系数,所以直接跳过第二步,第三步再插入一个1,说明这一行的系数插入完成了。接着将处于队首的系数进行一个出队操作,,将出队的数据打印出来,打印区就打印出了杨辉三角的第一行系数。
第三行,第一步插入1;
第二步,出队的元素是1,打印出来,并用一个变量x保存起来,即x = 1,此时队首也是1,用t保存起来,那么两者相加就是1+1 = 2,将相加结果用temp保存起来,并插入队列中。
第三步,第三行最后一个系数是1,因此H插入1;
第四步,将此时处于队首的元素,此时是1,打印出来。
看,打印区已经打印除了杨辉三角的前两行,第三行还留在队列中没有打印出来,因为第四行的系数计算需要第三行的数据,现在还不能出队打印。
第四行,第一步,插入1;
第二步,出队并打印一个元素,此时是1,用变量x保存,即x = 1,再加上此时队首是2,用变量t保存,即t = 2,两者相加用temp保存起来,即temp = 1+2 = 3,入队。由于第四行中间需要相加的元素有两个,因此第二步需要执行两次。
即出队并打印一个元素,此时就是2了,用变量x保存起来,即x = 2,此时的队首就不再是2了,因为2已经被出队了,此时队首是1,用变量t保存,即t = 1,两者相加的结果用temp保存起来并出队,即temp = 2 + 1 = 3;
第三步,出队一个1,杨辉三角第四行元素再队列中存储完毕;
第四步,将此时处于队首的元素出队打印,也就是1;
现在,就已经打印出了杨辉三角的前三行了,到现在可能有些朋友发现一个问题了,就是无论想要打印几行的杨辉三角,最后一行肯定是被留在队列中的,那么总体的最后一步,就是需要把队列中剩下的元素全部依次出队打印出来,那么四行的杨辉三角就被完全打印出来了。
以下是头文件:
#ifndef __YANFHUI_TRIANGLE_H
#define __YANFHUI_TRIANGLE_H
#define TRUE 1
#define FALSE 0
//定义结点,结点中除了可以存放数据,还有一个指向下一个结点的指针next
typedef struct node
{
int data;
struct node *next;
}LinkQueueNode;
typedef struct
{
LinkQueueNode *front; //表示队首结点
LinkQueueNode *rear; //表示队尾结点
}LinkQueue;
int InitQueue(LinkQueue *Q); //队列初始化
int EmptyQueue(LinkQueue *Q); //判断队列是否为空
int EnterQueue(LinkQueue *Q,int data); //入队
int DeleteQueue(LinkQueue *Q,int *data);//出队
int ClearQueue(LinkQueue *Q); //清除队列
int GetHead(LinkQueue *Q,int *data); //获取队首元素
int PrintQueue(LinkQueue *Q); //打印队列
void YangHuiTriangle(void); //构建杨辉三角
#endif
以下是.c文件
#include "yanghui_triangle.h"
#include <stdio.h>
#include <stdlib.h>
/*需要注意的是,front头节点不放任何元素,
输入的元素都是从front之后的第一个结点开始放的
*/
int InitQueue(LinkQueue *Q)
{
Q->front = (LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(Q->front != NULL)
{
Q->rear = Q->front;
Q->front->next = NULL;
printf("队列初始化成功!\n");
return TRUE;
}
else
return FALSE;
}
int EmptyQueue(LinkQueue *Q)
{
if(Q->front == Q->rear)
{
return FALSE;
}
return TRUE;
}
int EnterQueue(LinkQueue *Q,int data)
{
LinkQueueNode *NewNode;
NewNode = (LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(NewNode != NULL)
{
NewNode->data = data;
NewNode->next = NULL;
Q->rear->next = NewNode;
Q->rear = NewNode;
return TRUE;
}
else
return FALSE;
}
int DeleteQueue(LinkQueue *Q,int *data)
{
LinkQueueNode *p;
if(Q->front == Q->rear)
return FALSE;
p = Q->front->next;
Q->front->next = p->next;
*data = p->data;
free(p);
return TRUE;
}
int ClearQueue(LinkQueue *Q)
{
LinkQueueNode *p,*temp;
if(Q->front == Q->rear)
return FALSE;
temp = Q->front->next;
while(temp)
{
p = temp;
temp = p->next;
free(p);
}
temp = Q->front;
Q->front = Q->rear = NULL; //这一步骤是必要的,必须要让front和rear有确定的归属,如果没有,删除掉头节点后,front和rear就成了野指针,指向哪里都有可能
free(temp);
return TRUE;
}
int GetHead(LinkQueue *Q,int *data)
{
if(Q->front != NULL)
{
*data = Q->front->next->data;
return *data;
}
}
int PrintQueue(LinkQueue *Q)
{
LinkQueueNode *p,*temp;
if(Q->front == NULL)
{
printf("队列为空,无法打印!\n");
return FALSE;
}
temp = Q->front->next;
while(temp)
{
p = temp;
temp = p->next;
printf("%d ",p->data);
}
return TRUE;
}
//打印杨辉三角
void YangHuiTriangle(void)
{
int line;
int x; //存放出队的数据
int t; //存放首部即将出队的数据
int temp;
LinkQueue Queue;
InitQueue(&Queue);
printf("请输入打印行数:");
scanf("%d",&line);
EnterQueue(&Queue,1);
for(int n=2;n<=line;n++)
{
EnterQueue(&Queue,1);
for(int i=0;i<n-2;i++)
{
DeleteQueue(&Queue,&x);
printf("%d ",x);
GetHead(&Queue,&t);
temp = x + t;
EnterQueue(&Queue,temp);
}
EnterQueue(&Queue,1);
DeleteQueue(&Queue,&x);
printf("%d ",x);
printf("\n");
}
while(EmptyQueue(&Queue) != FALSE)//判断非空
{
DeleteQueue(&Queue,&x);
printf("%d ",x);
}
}
以下是主函数文件:
#include <stdio.h>
#include "yanghui_triangle.h"
int main()
{
/*
int data,e;
LinkQueue Queue;
InitQueue(&Queue);
for(int i=0;i<5;i++)
{
printf("请输入一个数字:");
scanf("%d",&data);
EnterQueue(&Queue,data);
PrintQueue(&Queue);
}
DeleteQueue(&Queue,&e);
printf("删除的数是:%d\n",e);
PrintQueue(&Queue);
GetHead(&Queue,&e);
printf("首元素是:%d\n",e);
PrintQueue(&Queue);
DeleteQueue(&Queue,&e);
printf("删除的数是:%d\n",e);
PrintQueue(&Queue);
printf("删除队列\n");
printf("%d\n",__LINE__);
ClearQueue(&Queue);
printf("队列删除成功!\n");
PrintQueue(&Queue);
printf("\n");
*/
printf("开始打印杨辉三角\n");
YangHuiTriangle();
return 0;
}
以下是编译结果:
主函数文件中,用/* */注释掉的那部分是我用来测试写出来的队列是否正确的测试代码,可以不用理会。