摘要

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

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

解决方案


方法一:暴力法

算法

在暴力法中,我们将会把所有可能爬的阶数进行组合,也就是 1 和 2 。而在每一步中我们都会继续调用 这个函数模拟爬 阶和 阶的情形,并返回两个函数的返回值之和。

其中 定义了当前阶数,而 定义了目标阶数。

复杂度分析

  • 时间复杂度:。树形递归的大小为

    在 n=5 时的递归树将是这样的:

    Climbing_Stairs

  • 空间复杂度:。递归树的深度可以达到


方法 2:记忆化递归

算法

在上一种方法中,我们计算每一步的结果时出现了冗余。另一种思路是,我们可以把每一步的结果存储在 数组之中,每当函数再次被调用,我们就直接从 数组返回结果。

数组的帮助下,我们得到了一个修复的递归树,其大小减少到

复杂度分析

  • 时间复杂度: 。树形递归的大小可以达到

  • 空间复杂度: 。递归树的深度可以达到


方法 3:动态规划

算法

不难发现,这个问题可以被分解为一些包含最优子结构的子问题,即它的最优解可以从其子问题的最优解来有效地构建,我们可以使用动态规划来解决这一问题。

阶可以由以下两种方法得到:

  1. 在第 阶后向上爬一阶。

  2. 在第 阶后向上爬 阶。

所以到达第 阶的方法总数就是到第 阶和第 阶的方法数之和。

表示能到达第 阶的方法总数:

示例:

!?!../Documents/70_Climbing_Stairs.json:1000,563!?!

复杂度分析

  • 时间复杂度:,单循环到

  • 空间复杂度: 数组用了 的空间。


方法 4: 斐波那契数

算法

在上述方法中,我们使用 数组,其中 。可以很容易通过分析得出 其实就是第 个斐波那契数。

现在我们必须找出以 作为第一项和第二项的斐波那契数列中的第 个数,也就是说

复杂度分析

  • 时间复杂度:。单循环到 ,需要计算第 个斐波那契数。

  • 空间复杂度:。使用常量级空间。


方法 5: Binets 方法

算法

这里有一种有趣的解法,它使用矩阵乘法来得到第 个斐波那契数。矩阵形式如下:

。按照此方法,第 个斐波那契数可以由 给出。

让我们试着证明一下。

我们可以使用数学归纳法来证明这一方法。易知,该矩阵给出了第 项(基本情况)的正确结果。由于 。这证明基本情况是成立的。

假设此方法适用于查找第 个斐波那契数,即 ,那么:

现在,我们需要证明在上述两个条件为真的情况下,该方法可以有效找出第 个斐波那契数,即,

证明:.

从而, 。得证。

我们需要为我们的问题做的唯一改动就是将斐波那契数列的初始项修改为 2 和 1 来代替原来的 1 和 0 。或者,另一种方法是使用相同的初始矩阵 并使用 得出最后结果。发生这种情况的原因是我们必须使用原斐波那契数列的第 2 项和第 3 项作为初始项。

复杂度分析

  • 时间复杂度:。遍历 位。

  • 空间复杂度:。使用常量级空间。

对时间复杂度的证明:

假设我们需要计算矩阵 次幂。假设, 是 2 的幂。因此,,其中 表示自然数的集合(包括 0)。我们可以用树形结构进行表示:

Climbing Stairs

这意味着:

所以,要计算 ,我们先要计算 并将其与自身相乘。而为了计算 ,我们需要对 进行类似的操作,并依此类推。

显然,树的高度为

让我们来估计一下 计算所需要的时间。矩阵 在幂运算过程中大小保持不变。因此,进行两矩阵幂乘的空间复杂度是 。我们需要执行 次这样的乘法。所以,计算 的时间复杂度为

如果数字 不是2的幂,我们可以使用其二进制表示将其转化为 2 的幂次项之和:

因此,我们可以使用以下方法获得最终结果:

这是我们在实现中使用的方法。 同样,时间复杂度仍为 ,因为乘法次数的限制为


方法 6: 斐波那契公式

算法

我们可以使用这一公式来找出第 个斐波那契数:

对于给定的问题,斐波那契序列将会被定义为 。尝试解决这一递归公式的标准方法是设出 ,其形式为 。然后,自然有 ,所以方程可以写作 。如果我们对整个方程进行约分,可以得到 或者写成二次方程形式

对二次公式求解,我们得到:

一般解采用以下形式:

时,有

时,有

解上述等式,我们得到:

的这些值带入上述的一般解方程中,可以得到:

复杂度分析

  • 时间复杂度: 方法将会用去 的时间。

  • 空间复杂度:。使用常量级空间。