小鱼儿要上一个n阶的台梯,他每次可以上1阶或2阶,问他上n阶的共有多少种方法,比如n等于3,输出3
分析
台阶数n | 取值种类 | 方法数S |
1 | (1) | 1 |
2 | (1,1),(2) | 2 |
3 | (1,1,1),(1,2),(2,1) | 3 |
4 | (1,1,1,1),(1,1,2),(1,2,1),(2,1,1),(2,2) | 5 |
5 | (1,1,1,1,1),(1,1,1,2),(1,1,2,1),(1,2,1,1),(1,2,2),(2,1,1,1),(2,1,2),(2,2,1) | 8 |
6 | (1,1,1,1,1,1),(1,1,1,1,2),(1,1,1,2,1),(1,1,2,1,1)(1,1,2,2),(1,2,1,1,1),(1,2,1,2),(1,2,2,1), (2,1,1,1,1),(2,1,1,2),(2,1,2,1),(2,2,1,1),(2,2,2) | 13 |
7 | (1,1,1,1,1,1,1),(1,1,1,1,1,2),(1,1,1,1,2,1),(1,1,1,2,1,1),(1,1,1,2,2),(1,1,2,1,1,1),(1,1,2,1,2), (1,1,2,2,1),(1,2,1,1,1,1),(1,2,1,1,2),(1,2,1,2,1), (1,2,2,1,1,),(1,2,2,2),(2,1,1,1,1,1),(2,1,1,1,2),(2,1,1,2,1),(2,1,2,1,1),(2,1,2,2),(2,2,1,1,1),(2,2,1,2),(2,2,2,1) | 21 |
8 | (1,1,1,1,1,1,1,1),(1,1,1,1,1,1,2),(1,1,1,1,1,2,1),(1,1,1,1,2,1,1),(1,1,1,1,2,2),(1,1,1,2,1,1,1),(1,1,1,2,1,2), (1,1,1,2,2,1),(1,1,2,1,1,1,1),(1,1,2,1,1,2),(1,1,2,1,2,1),(1,1,2,2,1,1),(1,1,2,2,2),(1,2,1,1,1,1,1),(1,2,1,1,1,2), (1,2,1,1,2,1),(1,2,1,2,1,1),(1,2,1,2,2),(1,2,2,1,1,1),(1,2,2,1,2),(1,2,2,2,1),(2,1,1,1,1,1,1),(2,1,1,1,1,2),(2,1,1,1,2,1) ,(2,1,1,2,1,1),(2,1,1,2,2),(2,1,2,1,1,1),(2,1,2,1,2),(2,1,2,2,1) ,(2,2,1,1,1,1),(2,2,1,1,2),(2,2,1,2,1),(2,2,2,1,1),(2,2,2,2) | 34 |
9 | ....... | 55 |
....... | ||
n-2 | ....... | S(n-2) |
n-1 | ....... | S(n-1) |
n | ....... | S(n-2)+S(n-1) |
由上表可以看出,对于给定一个台阶n,当台阶数目为n<3时,能上n阶台阶的方法为S = n,当台阶数n>=3时,能上台阶数S = S(n-1) + S(n-2).
解法1:递归算法
(1)当0 < n < 3,时 S(n) = n;
(2)当 n >= 3 时, S(n) = S(n - 1) + S(n - 2); 然而当 n - 2 > 1 时, S(n - 1) = S( n - 2) + S( n - 3)
(3)反复递归调用(1),(2)步骤组成的方法,直到最后传入的n值为<3时,即可求得结果(注:(1)为函数递归调用的出口)!
解法2: 采用保存先前计算的数据
将之前第一回计算的数据先保存到一个集合saveList中,下次需要时直接获取
比如计算f(5)=f(4)+f(3) ,f(3)=f(2)+f(1) ,f(4)=f(3)+f(2) ,f(3)=f(2)+f(1);
当计算f(5)时,采用从前往后方式进行计算,先计算f(1),f(2)将数据保存在saveList集合中,计算f(3)时
直接用计算saveList.get(0) + saveList.get(1)的值存入saveList中,此时saveList的大小为3,
同样计算f(4)时进行计算saveList.get(1) + saveList.get(2)的值存入saveList中,依次类推
最后返回saveList最后一个数据即可
相比与第一种方法,此处效率更高点
示例代码(仅供参考)
/**
* 小鱼儿要上一个n阶的台梯,他每次可以上1阶或2阶,问他上n阶的共有多少种方法,比如n等于3,输出3
*
* @author bianjie
*
*/
public class Test1 {
/**
* 统计方法1:采用递归算法
* 缺点:执行效率太低,存在重复计算问题,会出现栈溢出,
* 当输入的台阶步数step太大时,比如100,将会出现长时间等待计算问题
*
* 栈溢出就是指你一个函数递归太多次导致的栈内存溢出,那么分析为什么会递归太多次,
* 比如算f(5)=f(4)+f(3) f(3)=f(2)+f(1); f(4)=f(3)+f(2) f(3)=f(2)+f(1);
* 最后得出结果,这样算可以发现f(3)重复算了两次,如果给个更大的数就不用解释了,
* 这种写法时间复杂度是O(2的n次方),要解决的问题是重复计算
* @param step 台阶数
* @return 太阶数对应的可走的方法数
*/
public static int countMethod1(int step) {
int total = 0;
// 台阶步数小于3,递归函数的出口
if (step < 3) {
if (step == 1) {
total = 1;
} else if (step == 2) {
total = 2;
}
} else {
// 台阶数大于3,可走的方法数 S(n)= 比它小一个台阶的方法数S(n - 1)+比它小俩个台阶的方法数S(n - 2)
total = countMethod1(step - 1) + countMethod1(step - 2);
}
return total;
}
//数据缓存
public List<Integer> saveList = new ArrayList<Integer>();
/**
* 统计方法1:采用保存先前计算的数据
* 将之前第一回计算的数据先保存到saveList集合中,下次需要时直接获取
* 比如计算f(5)=f(4)+f(3) ,f(3)=f(2)+f(1) ,f(4)=f(3)+f(2) ,f(3)=f(2)+f(1);
* 当计算f(5)时,采用从前往后方式进行计算,先计算f(1),f(2)将数据保存在saveList集合中,计算f(3)时
* 直接用计算saveList.get(0) + saveList.get(1)的值存入saveList中,此时saveList的大小为3,
* 同样计算f(4)时进行计算saveList.get(1) + saveList.get(2)的值存入saveList中,依次类推
* 最后返回saveList最后一个数据即可
* 相比与第一种方法,此处效率更高点
* @param size 台阶步数
* @return 最后可走的方法数
*/
public int countMethod2(int step) {
saveList.add(1);
saveList.add(2);
if (step >= 3) {
for (int startIndex = 1; startIndex < step; startIndex++) {
int newData = saveList.get(startIndex) + saveList.get(startIndex - 1);
saveList.add(newData);
}
}
return saveList.get(step - 1);
}
public static void main(String[] args) {
// 台阶数
int step = 10000;
System.out.println("台阶数" + step + "个可走的种类数 " + (new Test1()).countMethod2(step) + "种");
}
}