杨辉三角超详细小解三种方法--《Blind-Stab》

   杨辉三角二项式系数在三角形中的一种几何排列。在欧洲,这个表叫做帕斯卡三角形帕斯卡(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、有志者,事竟成,一起加油,一起坚持!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值