1.Go!!!
编程题:有n步台阶,每次只能走一步或者走两步,则共有多少种不同的走法?
这个问题了话,我能想到的就是两种方法:1.递归;2.迭代。
这二者的区别在于:方法调用自身称为递归;利用变量的原值推出新值称为迭代。
2.递归
· n=0:没意义,台阶数不可能小于1。
· n=1:只能走一步直接到位,f(1)=1。
· n=2:①1+1;②2;f(2)=2。
· n=3:①1+1+1;②1+2;③2+1;f(3)=3 = f(2) + f(1)。
· n=4:①1+1+1+1;②1+2+1;③1+1+2;④2+1+1;⑤2+2;f(4)=5 = f(3) + f(2)。
· n=5:①1+1+1+1+1;②1+2+1+1;③1+1+2+1;④2+1+1+1;⑤1+1+1+2;⑥1+2+2;⑦2+1+2;⑧2+2+1;f(5)=8 = f(4) + f(3)。
· ......
· n=x:f(x) = f(x-1) + f(x-2)
规律已经可以看出来了,从n=3开始,后面的每一项都等于它对应的前两项之和。
这里想说一下,不要说这个就是斐波那契数列,斐波那契数列的前几项是 0、1、1、2、3、5、8......,你们学过斐波那契数列吗?和这个一样吗???合适的说法还是类似于斐波那契数列。(不要说这就是斐波那契数列,让别人听了误以为真,忽略了斐波那契数列前面还有两项0和1,数学的奇妙之处就在于严谨。你也别扯什么这是计算机不是什么数学,你仔细想想计算机是不是基于数学的?它们二者谁先诞生?)
import java.util.Scanner; /** * */ public class Step1 { public static long f(long n) { if (n < 1) { throw new IllegalArgumentException("台阶数n不能小于1"); } if (n == 1 || n == 2) { return n; } return f(n-1) + f(n-2); } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); long start = System.currentTimeMillis(); long n = scanner.nextLong(); System.out.println(f(n)); long end = System.currentTimeMillis(); System.out.println("程序运行总耗时: " + (end - start)); } }
3.迭代
这里首先我们需要三个临时变量one、two、result,其中one保存的是最后走一步的情况,two保存的是最后走两步的情况,result是最终的结果。
· n=0:没意义,台阶数不可能小于1。
· n=1:只能走一步直接到位,f(1)=1。
· n=2:①1+1;②2;f(2)=2。
· n=3:①先到达f(1),然后从f(1)直接走两步到达,即此时f(1)=two;②先到达f(2),然后从f(2)直接走一步到达,即此时f(2)=one;所以f(3)=one + two。
· n=4:当n=3时,two表示最后走两步的情况,而当n=4,台阶数多了一阶,这个时候n=3时的那个one(最后走一步)就变成了最后要走两步,即 two = one;
而n=3时的那个one表示最后走一步的情况,此时n=4,台阶数多了一阶,这个时候n=3时的那个result(最终结果)就变成了最后要走一步,即 one = result。
import java.util.Scanner; /** * */ public class Step2 { public static long f(long n) { if (n < 1) { throw new IllegalArgumentException("台阶数n不能小于1"); } if (n == 1 || n == 2) { return n; } long one = 2; //初始化为走到第二级台阶的走法(保存最后走一步) long two = 1; //初始化为走到第一级台阶的走法(保存最后走两步) long result = 0; for (long i = 3; i <= n; i++) { //最后走一步 + 最后走两步的走法 result = one + two; two = one; one = result; } return result; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); long start = System.currentTimeMillis(); long n = scanner.nextLong(); System.out.println(f(n)); long end = System.currentTimeMillis(); System.out.println("程序运行总耗时: " + (end - start)); } }
最终对比递归和迭代来看:
递归:
· 优点:大问题转化为小问题,可以减少代码量,同时代码精简,可读性好。
· 缺点:递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。
迭代:
· 优点:代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销。
· 缺点:代码比递归要复杂,同时可读性不如递归。