首先看一道编程题如下:
有 n 步台阶,一次只能上 1 步或者 2 步,共有多少种走法?
/**
* 递归
* 编程题:有 n 步台阶,一次只能上 1 步或者 2 步,共有多少种走法
*
* 递归
* 分析如图,当 n 等于 1 或者 2 时,走法就等于 n,从第三层台阶开始,每一层台阶为前两层台阶走法之和。
* .n=1 ->一步 ->f(1)=1
* ·n=2 ->(1)一步一步(2)直接2步 ->f(2)=2
* ·n=3 ->(1)先到达f(1),然后从f(1)直接跨2步 ->f(3)=f(1)+f(2)
* (2)先到达f(2),然后从f(2)跨1步
* ·n=4 ->(1)先到达f(2),然后从f(2)直接跨2步 ->f(4)=f(2)+f(3)
* (2)先到达f(3),然后从f(3)跨1步
*
* ·n=x -> (1)先到达子(x-2),然后从f(-力真接跨2步 ->f(x)=f(x-2)+f(x-1)
* (2)先到达f(x-1),然后从f(x-1)跨1步
*/
public class Recursion {
@Test
public void test() {
// 时间复杂度 ...
long start = System.currentTimeMillis();
System.out.println(recursion(40)); // 165580141
long end = System.currentTimeMillis(); // 537
System.out.println(end - start);
}
// 递归实现
public int recursion(int n) {
if(n < 1) {
return 0;
}
if(n == 1 || n == 2) {
return n;
}
return recursion(n - 2) + recursion( n - 1);
}
}
/**
* 迭代
* 用 one、two 这两个变量来存储 n 的最后走一步和最后走两步,从第三层开始走,用 sum 来保存前两次的走法的次数,sum = two + one; 然后 two 移到 one,one 移到 sum 循环迭代。
*
* ·n=1->(1)一步 ->f(1)=1
* ·n=2->(1)一步一步(2)直接2步 ->f(2)=2
* ·n=3->(1)先到达f(1),然后从f(1)直接跨2步 ->f(3)= two + one
* (2)先到达f(2),然后从f(2)跨1步 f(3)=f(1)+f(2)
* two=f(1);one=f(2)
* ·n=4->(1)先到达f(2),然后从于(2)直接跨2步 ->f(4)= two + one
* (2)先到达f(3),然后从f(3)跨1步 f(4)=f(2)+f(3)
* two=f(2);one=f(3)
* ·n=x->(1)先到达f(x-2),然后从f(x-2)直接跨2步->f(x)= two + one
* (2)先到达f(x-1),然后从f(x-1)跨1步 f(x)=f(x-2)+f(x-1)
* two=f(x-2);one=f(x-1)
*/
public class Iteration {
@Test
public void test() {
// 时间复杂度 O(n)
long start = System.currentTimeMillis();
System.out.println(iteration(40)); // 165580141
long end = System.currentTimeMillis(); // 0
System.out.println(end - start);
}
// 迭代实现
public int iteration(int n) {
if(n < 1) {
return 0;
}
if(n == 1 || n == 2) {
return n;
}
int two = 1; // 一层台阶,有 1 走法, n 的前两层台阶的走法
int one = 2; // 二层台阶,有 2 走法, n 的前一层台阶的走法
int sum = 0; // 记录一共有多少中走法
for(int i = 3; i <= n; i++) {
sum = two + one;
two = one;
one = sum;
}
return sum;
}
}
总结:
1)方法调用自身称为递归,利用变量的原值推出新值称为迭代。
2)递归
优点:大问题转化为小问题,可以减少代码量,同时代码精简,可读性好;
缺点:递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。
3)迭代
优点:代码运行效率好,因为时间复杂度为 O(n),而且没有额为空间的开销;
缺点:代码不如递归简洁。