杨辉三角,是二项式系数在三角形中的一种几何排列。在欧洲,这个表叫做帕斯卡三角形。帕斯卡(1623----1662)是在1654年发现这一规律的,比杨辉要迟393年,比贾宪迟600年。杨辉三角是中国古代数学的杰出研究成果之一,它把二项式系数图形化,把组合数内在的一些代数性质直观地从图形中体现出来,是一种离散型的数与形的结合 .
今天我们就来用三种解法小解杨辉三角:二维数组,一维数组,递归;
现在我们来分析一下怎么用Java写杨辉三角:
观察发现有这几个规律:
1、每一行的第一个和最后一个是1.
2、每一行的第i个元素值 = 上一行第i-1个元素值+上一行第i个元素值。换成二维数组即YH[n][i] = YH[n-1][i-1] + YH[n-1][i];.
3、第一行特殊初始化1.
4、每行元素个数 = 行数值.
方法一:二维数组
通过上面的分析出的发现YH[n][i] = YH[n-1][i-1] + YH[n-1][i]结论能很容易发现用二维数组写是可以的,我们根据输入的需要打印的N行杨辉三角去创建一个二维数组YH[N+1][N+1],之所以要多创建一个长度为N+1而不是N的二维数组,是因为第0行是没有打印的,是从第1行开始打印,这才符合我们正常的逻辑,双层for循环,同时要控制每行第一个元素和最后一个元素都为1.
下面看代码:
public class YangHui {
public static void main(String[] args) {
//录入要打印的杨辉三角行数
System.out.print("请输入要打印的杨辉三角行数:");
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
//创建二维数组
int [][] YH = new int [N+1][N+1];
//遍历行数
for (int i = 0; i < YH.length; i++) {
//第i行元素遍历
for (int j = 0; j < i; j++) {
//控制每行首尾皆为1
if (j==0||j==i) {
YH[i][j] = 1;
}else {
//非首尾位遵守此公式
YH[i][j] = YH[i-1][j-1] + YH[i-1][j];
}
}
}
for (int i = 0; i < YH.length; i++) {
//控制输出格式,像一三角形的
for (int k = 0; k < N - i; k++) {
System.out.print(" ");
}
//打印
for (int j = 0; j < i; j++) {
System.out.printf("%4d",YH[i][j]);
}
System.out.println("");
}
}
}
方法二:一维数组
规律还是那个规律,但是一维数组很明显更加的节省空间,而且更加巧妙。
输入打印行数N,创建一个长度为N+1的一维数组YH[N+1];因为我们要的到第i行的值,那么必然要知道第i-1行所有元素的值,这样的话我们就设计一个算法,就让这个一维数组可以实现这样的功能。
长度为N+1的数组,被使用的只有N个长度,角标为0的空间是没有被使用的。初始化为N+1个0,可以看作第0行。
那么接下来设置一个变量previousnum;代表当前要求行row对应角标 i 的上一行row - 1行对应角标 i 的值。其实和二维数组解法是一样的,只不过这里利用变量一直刷新罢了。初始化priviousnum = 1;设置一个变量currentnum,代表previousnum右边的一个元素,其实也就相当于指针,因为求得当前行元素必然要利用上一行元素,这样就利用两个移动的指针从左往右移动,不停的刷新两值。如下图,拿第5行到第6行的变化来说:
这样一来,打印每一行的初始到结束,previousnum 和 currentnum的值都会初始化为0,1;并且previousnum和currentnum两个变量的移动无非就是实现下面代码段的一个过程:
for (int i = 1; i <= N; i ++){
for (int j = 1; j <= i; j++){
int current = arr[j];
arr[j] = previous + current;
previous = current;
}
}
这样就很好理解了,一维数组就是重复利用,不停刷新,同时不停前进更新的过程,本质还是利用上一行的值,但是实现的很巧妙。
具体一维数组代码实现:
public class YHarr {
public static void main(String[] args) {
System.out.print("请输入要打印的杨辉三角行数:");
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
YH(N);
}
public static void YH(int N) {
//创建一维数组
int[] arr = new int[N + 1];
//初始化previousnum
int previousnum = 1;
//打印N行
for (int i = 1; i <= N; i++) {
//用于测试,观察每次arr数组内元素值的变化助于发现规律
System.out.print(Arrays.toString(arr));
//用于控制格式输出
for (int k = 0; k < N - i; k++) {
System.out.print(" ");
}
//关键实现点,实现指针移动
for (int j = 1; j <= i; j++) {
int currentnum = arr[j];
arr[j] = previousnum + currentnum;
previousnum = currentnum;
System.out.printf("%4d", arr[j]);
}
//换行
System.out.println();
}
}
}
方法三:递归实现杨辉三角
hh,一般循环,有规律的问题解决,怎么可以少的了递归呢?
之前就是说过,关于递归,无非就是找到递归中的递的条件进入,再找到递归中的归的条件跳出;并且将问题看成一个整体,忽略局部,让程序自己去找答案。也就是把逻辑写出来,让他自己去递归,求得答案。
很明显,进入递归的条件是,,就是直接进入嘛,或者说还没到顶,上面还有元素,还能继续向上递。而归的条件是,当行数推倒第一行了,或者当前行已经求到最后一行了。
具体点说呢就是,观察发现,当前所求行的所求元素值,是其上一行的此角标和后一个角标相加的和。那么我们只要写一个递归函数YH_print1(),它实现的是返回当前所求值替换为上一行两个值的YH_print1()表达式。当然在这之前我们必须找到此递归函数的跳出条件也就是归的条件,是啥?那就是每行第一个值和最后一个值,都是1,最后一个值对应的角标与行数相等,这个规律我们早都发现了。其中此函数的参数为行和列,分别为row和col。也就是说,当当col==1时,也就是每行的第一个元素,我们就别返回递归函数表达式了,直接返回1即可。如果是最后一个也就是col == row时,同样的返回1。这就是递归,那么此函数部分实现可以这样写即:
public static int YHprint_1(int row,int col) {
if (col == 1 || col == row) {
return 1;
}
return YHprint_1(row - 1,col-1) + YHprint_1(row - 1,col);
}
具体的递归写法代码实现呢就是:
public class YangHui {
public static void main(String[] args) {
System.out.print("请输入要打印的杨辉三角行数:");
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
//开始每行的打印
for (int i = 1; i <= N; i++) {
//格式控制,打印前面的空格
for (int k = 0; k < N - i; k++) {
System.out.print(" ");
}
//对于第i行的第j个元素,调用递归函数求其值
for (int j = 1; j <= i; j++) {
System.out.printf("%4d",YHprint_1(i, j) );
}
System.out.println("");
}
}
//递归函数的实现
public static int YHprint_1(int row,int col) {
//每行第一个和最后一个,不返回递归函数的调用,返回具体值,结束该轮调用到头,跳出
if (col == 1 || col == row) {
return 1;
}
//没有完结,不是第一个,不是最后一个,递归调用继续,同时行数减一即往上一行走
return YHprint_1(row - 1,col-1) + YHprint_1(row - 1,col);
}
}
三种方法,打印结果都一样的:
其实呢,肯定不止三种方法,还有很多很多种写法。但是还是那句话,万变不离其宗,不变其魂!无论怎么变,杨辉三角的产生规则是不会变的,就是上一行两个相加,仅此而已。
总结:
1、万变,不离其综,不变其魂。无论解法在怎么变,在怎么多,基础规则是不变的。
2、二维数组解杨辉最容易懂,最容易理解,但是最low,耗费的空间大,一维数组的思想可以学习,类似指针移动刷新替换,而递归就没什么好说的,其实看着和二维数组是一样的。
3、关于打印杨辉三角还有一些问题,就是创建数组时老是多创建了一个长度,那时因为角标为0的那一行根本打印不了,不然就会越界,所以少了不多给一个长度的话就老是会少一行,所以每次多增加一个长度。也就是说,其实在用数组打印杨辉三角时,起始位置和终点位置要注意,其实就是小于还是等于,从0开始遍历还是从1开始遍历的问题。
4、有志者,事竟成,一起加油,一起坚持!