爬楼梯

  • 题目
  • 函数原型
  • 边界判断
  • 算法设计:递归
  • 算法设计:递归+记忆化搜索
  • 算法设计:动态规划
  • 算法设计:递推



 


题目

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。

你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

 


函数原型

C的函数原型:

int climbStairs(int n){}

 


边界判断

int climbStairs(int n){
	if( n < 0 )
		return 0;
}

 


算法设计:递归

题目就是,计算【斐波纳妾数列】。

斐波纳妾数列的定义:

  • [70].爬楼梯_递归
  • [70].爬楼梯_记忆化搜索_02
  • [70].爬楼梯_递归_03

这是一个天然的递归表达式,可以很直观用递归表示出来。

递归是一个视角,即从后往前看,具体的介绍请看:《递归》。

/* 递归 */
int climbStairs(int n){
    if( n == 0 || n == 1 )	
    // n == 0 是没有一阶台阶,也有一种可能,或者写为 n <= 2  return n;
        return 1;
 
    return climbStairs(n-1) + climbStairs(n-2);
}

递归的复杂度:

  • 时间复杂度:[70].爬楼梯_算法设计_04
  • 空间复杂度:[70].爬楼梯_记忆化搜索_05

但提交上去,超出时间限制了。

这是为啥?
 


算法设计:递归+记忆化搜索

因为在递归计算【斐波纳妾数列】的过程中,有大量的重复计算。

第一个数有重复计算:

[70].爬楼梯_算法设计_06


第二个也有重复计算:

[70].爬楼梯_记忆化搜索_07


为了避免重复计算,我们可以用个数组保存计算得到的值,当再次需要这个值时,只需要查找即可。

int memo[1024];    // 全局变量,每个元素默认为 0
 
/* 递归 + 记忆化搜索 */
int climbStairs(int n){
    if( n == 0 || n == 1 )
        return 1;
 
    if( memo[n] == 0 )   // 没有计算过,就计算,并把结果存储到 memo[] 
        memo[n] = climbStairs(n-1) + climbStairs(n-2);
 
    return memo[n];      // 计算过的话,直接返回
}

这种避免重复的计算方法,也称为【记忆化搜索】。

递归:从后往前看,使用记忆化搜索是因为递归里有大量的重复,使用可以避免重复计算。

递归+记忆化搜索的复杂度:

  • 时间复杂度:[70].爬楼梯_记忆化搜索_05
  • 空间复杂度:[70].爬楼梯_记忆化搜索_05
     

算法设计:动态规划

而动态规划就是采用递推的方法,从前往后解决问题,从解决小问题开始,再到整体的问题求解。

/* 动态规划 */
int climbStairs(int n){
    int memo[n+1];
    memset(memo, 0, sizeof(int) * n+1);
 
    memo[0] = 1;
    memo[1] = 1;
    for( int i=2; i<=n; i++ )
        memo[i] = memo[i-1] + memo[i-2];
 
    return memo[n]; 
}

动态规划:将原问题拆解为若干个子问题,同时保存子问题的答案,使得每个子问题的只求解一次,最终获得原问题的答案。

回顾一下,我们是如何计算【斐波纳妾数列】的:

[70].爬楼梯_递归_10


采用直观的递归算法,但因为递归有重复计算,所以引进记忆化搜索。

整个计算过程是自顶向下,从后往前推的,如果要达到第 [70].爬楼梯_算法设计_11 阶,就先要到达第 [70].爬楼梯_算法设计_12 阶 or 第 [70].爬楼梯_递归_13 阶,要达到第 [70].爬楼梯_算法设计_12

[70].爬楼梯_记忆化搜索_15


动态规划的复杂度:

  • 时间复杂度:[70].爬楼梯_记忆化搜索_05
  • 空间复杂度:[70].爬楼梯_记忆化搜索_05
     

算法设计:递推

递推,即从前往后看。

int climbStairs(int n){
    if ( n < 0 ) return 0;
    if (n <=2 ) return n;

    int fn1 = 1, fn2 = 2;
    int fn3;
    for (int i = 2 ;i < n; i++){
        fn3 = fn2 + fn1;
        fn1 = fn2;
        fn2 = fn3;
    }
    return fn3;

}

递推的复杂度:

  • 时间复杂度:[70].爬楼梯_记忆化搜索_05
  • 空间复杂度:[70].爬楼梯_递归_19