在楼梯上的递归

时间:2014-12-24 13:28:30

标签: recursion dynamic-programming

我试图理解书中提供的解决方案以下问题:

“一个孩子正在爬楼梯,有n个台阶,可以一步一跳,两步或三步。实施一种方法来计算孩子爬楼梯的方式。”

本书的解决方案如下,源于这样的事实:“最后一步可能是从n - 1的单步跳跃,从步骤n - 2的双步跳跃或从步骤n - 3的三步跳跃”

public static int countWaysDP(int n, int[] map) {
    if (n < 0) 
        return 0;
    else if (n == 0)
        return 1;
    else if (map[n] > -1)
        return map[n];
    else {
        map[n] = countWaysDP(n - 1, map) + countWaysDP(n - 2, map) + countWaysDP(n - 3, map);
        return map[n]; }
}

我的困惑是:

  1. 如果步数为零,为什么程序会返回1?我想到它的方式,如果步数为零,则有零路穿过楼梯。难道更好的解决方案不是像“if(n&lt; = 0)返回0;否则if(n == 1)返回1”?

  2. 我不确定我理解这个静态方法背后的理由吗?谷歌说静态方法是由整个类调用的方法,而不是类的对象。所以这本书的意图似乎是这样的:

  3. class Staircase {
        static int n;
    public:
        static int countWaysDP(int n, int[] map); }
    

    而不是:

    class Staircase {
        int n;
    public:
        int countWaysDP(int n, int[] map); }
    

    为什么呢?这个类有多个楼梯实例化的问题是什么?

    感谢。

    (注:书籍正在破解编码面试)

4 个答案:

答案 0 :(得分:2)

回答你的第一个问题,它变成了数学之美:如果楼梯有一步,有一种方法可以解决它。如果有0个步骤,还有一种方法可以解决它,这是无所作为的。

对于n步楼梯来说,m次,你可以步行1,2或3步完成它。因此,如果n为1,则m为1,并且有1路。如果n为0,则m为0,并且还有1种方式 - 完全不采取任何步骤的方式。

如果你写出两步楼梯的所有方法,它是[[1, 1], [2]],对于一步楼梯,它是[[1]],对于0阶梯,它是{{ 1}},而不是[[]]。数组[]内的元素数是1,而不是0。

如果问题是你可以走1步或2步,这将成为斐波那契系列。注意fib(0)= 1和fib(1)= 1,它对应于同一个东西:当阶梯是1步时,有1种方法来解决它。当有0个步骤时,有1种方法可以解决它,而且它什么都不做。事实证明走两步楼梯的方法是fib(2)是2并且它等于fib(1)+ fib(0)= 1 + 1 = 2,如果它不起作用,它将不起作用fib(0)等于0.

答案 1 :(得分:1)

答案2: 静态方法意味着该函数不需要来自对象的任何信息。

该函数只接受一个输入(在参数中),处理它并返回一些东西。 当你没有看到任何&#34;这个&#34;在函数中,您可以将其设置为静态。

非静态方法通常会读取某些属性(此变量)和/或在某些属性中存储值。


答案1: 我将其转换为javascript,只是为了显示会发生什么。

http://jsbin.com/linake/1/edit?html,js,output

我想这就是重点。递归通常与您期望的相反。它通常以相反的顺序返回值。 5个楼梯: 首先它返回n = 1;那么n = 2,......直到n = 5;
n = 5必须等到n = 4准备好,n = 4必须等到n = 3准备就绪,......

所以这是你的n = 0和n <0: 函数的第一次返回有n = 1;这称之为

map[n] = countWaysDP(n - 1, map) + countWaysDP(n - 2, map) + countWaysDP(n - 3, map)

这就是

map[n] = countWaysDP(0, map) + countWaysDP(-1, map) + countWaysDP(-2, map)

有countWaysDP(0,map)返回1;其他术语没有意义,所以它们返回0.这就是为什么n == 0和n <0

这些子句的原因

注意,你可以添加

+ countWaysDP(n - 4, map)

如果你想看看当孩子也能跳4个案件时会发生什么

另请注意: 正如我在回答2中所说,你看到这个功能并不需要任何对象。它只处理数据并返回一些东西。 所以,在你的情况下,在你的类中使用这个函数是有用的,因为你的函数被分组(它们不只是散布在脚本周围的松散函数),而是使它静态意味着编译器没有必要随身携带对象的内存(尤其是对象的属性)。

我希望这是有道理的。肯定有人可以提供更准确的答案;我对我的元素有点回答(我主要是做javascript)。

答案 2 :(得分:0)

为了尝试回答你的第一个问题,为什么它返回1而不是0,假设你正在看一个总共2步的楼梯,那么递归调用就会变成:

countWaysDP(2 - 1, map) + countWaysDP(2 - 2, map) + countWaysDP(2 - 3, map);

第二个递归调用是 n 变为零的那个,当我们找到一个成功的路径时,因为从2个步骤开始,显然有一个采取两个步骤的路径。现在,如果你按照你的建议写作:

n == 1: return 1 

你不会接受从两个阶梯走两步!该陈述的含义是,只有在一步结束时才计算路径!

答案 3 :(得分:0)

你需要考虑它有一个树,每个节点有3个可能的选项。 如果楼梯的大小是4 我们会有这样的事情:

(4)--1-->(3)--..(Choose a step and keep branching)...
  |__2-->(2)--..(Until you get size of zero)............
  |__3-->(1)--1-->(0) # <--Like this <--

最后,如果你计算所有大小为零的叶子,你将获得所有可能的方法。

所以你可以这样想,如果你采取措施,然后考虑像这个尺寸步骤更新楼梯的大小,你的步骤可以是(1,2,3)

这样做可以编写如下代码:

choices = (1, 2, 3)
counter = 0

def test(size):
    global counter

    if size == 0:
        counter += 1

    for choice in choices:
        if size - choice >= 0:
            test(size - choice)

    return counter