10阶层楼梯,每次只能走一阶或两阶,问有多少种走法?

问题:10阶层楼梯,每次只能走一阶或两阶,问有多少种走法?

拿到这个题目的第一想法是暴力穷举法,多个For循环,每一步都有两种走法,这种方法的时间复杂度为O(2^n),显然不合理。

然后,思考看是否有更合理的解法。

这个时候我们思考的方向应该是:动态规划(Dynamic Programming)。何为动态规划,即把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。

就拿这个题目来说,假设只差最后一步就可以走到第10阶台阶,这时候会出现几种情况?显然是两种,因为每一步只允许走1级或2级台阶。第一种是从9级到10级,第二种是从8级直接到10级。如果我们假设从0级到8级的走法有X种,从0级到9级的走法有Y种,那么0到10级的走法有多少种呢?

相信大家已经知晓答案了,0到10级别的走法为X+Y;也许有小伙伴会质疑X和Y中会有重复的走法,但是我们从整体走法来看,即使前面有重复的路线,却也因为最后一步的不同,而导致整体的是不一样的走法。为了便于表达,我们将10阶的走法简写为F(10),那么

F(10) = F(9)+F(8)

那么F(9)和F(8)如何计算呢?可得出如下的推导

F(1) = 1;

F(2) = 2;

F(n) = F(n-1)+F(n-2);

动态规划当中包含三个重要的概念:最优子结构、边界、状态转移公式

刚刚我们分析出F(10)=F(9)+F(8),因此F(9)和F(8)是F(10)的最优子结构。

当有一阶或两阶时,我们可以直接得出结果F(1) = 1,F(2)=2即为问题的边界。

F(n)=F(n-1)+F(n-2)是阶段与阶段间的转移方程。

我们可以试着用递归来实现:

private static int climbStairWays(int n) {
if(n < 1) {
return 0;
}
if(1 == n)
return 1;

if(2 == n)
return 2;

return climbStairWays(n-1) + climbStairWays(n-2);

}

这样的实现方式是简洁了,我们来看看该方案的时间复杂度,分析递归的方法不难发现,这是一颗二叉树,树的节点个数就是我们递归所需要计算的次数。二叉树的高度为N-1,节点的个数接近2的N-1次方。所以时间复杂度可以近似看做O(2^N).

那是否有办法进行优化一下呢?

可以自行画出二叉树分析一下,会发现,越往下走,重复的计算越多。那这些重复计算的结果我们是否可以先缓存起来,下次遇到的时候直接拿来用呢?

这里我们可以使用备忘录进行记录已经计算过的值和结果,如下

private static int climbStairWays2(int n,HashMap<Integer,Integer> map) {
if(n < 1) {
return 0;
}
if(n == 1){
return 1;
}

if(n == 2) {
return 2;
}

if(map.containsKey(n)) {
return map.get(n);
}else {
int value = climbStairWays2(n-1,map)+climbStairWays2(n-2,map);
map.put(n,value);
return value;

}

}

在以上的代码中,集合map是一个备忘录。每当需要计算F(N)的时候,会首先从map中寻找匹配元素,如果map中存在,就直接返回结果;不存在,就进行计算,将结果存入备忘录中。

我们再来看下时间复杂度和空间复杂度。从F(1)到F(N)一共有N个不同的输入,在Hash表里存了N-2个结果,所以时间复杂度和空间复杂度都是O(N).

这个时候感觉时间复杂度和空间复杂度已经都是O(N)级别了,时间复杂度是不能再小了,那么空间复杂度呢,能否再进一步减小?

我们之前用的都是递归,是一种自顶向下的求解过程,现在不妨进行思维逆转一下,自底往上求解。

通过上面的观察发现,F(1),F(2)是确定的,由这两个数可以直接得出F(3)=F(1)+F(2),由F(2)和F(3)又可以得出F(4)...

即后面的每个数字都是由前两个相邻的数字相加得来,那么每次计算完成之后,我们仅保留前面的两个状态即可完成所有的推导,代码如下

private static int climbStairWays3(int n) {
int count = 0;
int first = 1;
int second = 2;

if(n < 1) {
return 0;
}
if(n == 1) {
return 1;
}

if(n == 2) {
return 2;
}

for(int i = 3;i <= n;i++) {
count = first + second;
first = second;
second = count;
}
return count;

}

程序从i=3开始迭代,一直到i=n结束。每一次迭代,都会计算出多一级台阶的走法数量。迭代过程中只需保留两个临时变量first和second,分别代表上一次和上上次迭代的结果。这个时候的时间复杂度依然是O(N),但是空间复杂度却变为了O(1).

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值