这是一道简单题,题目要求如下:
其实这道题很简单,我第一次做的时候使用的方法就是动态规划,但是因为没有使用滚动数组,所以在空间复杂度上差了一些。所以写这篇博客来记录一下从这道题中汲取的精华,一是滚动数组,二是矩阵的快速幂算法,后者是从官方题解中学到的。
1.DP
首先记录动态规划+滚动数组的方法:
int climbStairs(int n) {
/*
allocate a DP array, DP[i] means
the number of methods to reach i
*/
/*some foundations*/
int LastTwo = 1;
int LastOne = 1;
int Present = 1;
/*start DP*/
for(int i = 2 ; i <= n ; ++i)
{
Present = LastTwo + LastOne;
LastTwo = LastOne;
LastOne = Present;
}
return Present;
}
这道题的思路是这样的,我们可以开一个DP数组来保存走到每一级楼梯的步数,比如DP[i]表示抵达第i级台阶的方法数。
随后是递推奠基,我们一开始就位于第0层,所以DP[0] = 1, DP[1] = 1。
这道题的递归方程就是DP[i] = DP[i - 1] + DP[i - 2],实际上也就是斐波那契数列,思路是这样的:
要考虑走到第i级楼梯的方法,那么考虑谁可以到达第i级楼梯,那么分别是i - 1和i - 2级楼梯都可以一步走到,那么走到第i级楼梯的方法就这两者之和了。
应该说到目前为止思路都还是比较常规的,但是这道题还有一个可以优化的地方在于,因为每一次计算只会用到相邻的前两个结果,而对更早的计算结果没有引用。那么我们完全没必要开一个完整的数组记录每一步的结果,只需要记录下来计算当前结果需要用得到的两个相邻值即可,这就是滚动数组的优化方案(如上代码所示),在N很大时可以极大地优化算法的空间复杂度。
2.Matrix Multiplication QuickPower
第二种方法就是矩阵乘法快速幂,这种方法需要用到一些数学知识才可以想到,但是它可以极大地约简算法的时间复杂度。这里主要总结快速幂算法,因为这道题一般人不会想到构造矩阵乘法来求解,快速幂算法如下所示:
int quickPower(int x, int n)
{
int Ans = 1;
while(n)
{
if(n & 1 == 1) // if the LSB of n is 1
Ans = Ans * x; // mod some number to prevent overflow
x = x * x;
n >> 1;
}
return Ans;
}
算法很简单,但是它可以使得求幂的算法时间复杂度从O(n)降至O(logn)。
算法的大致思想是幂次n看成其二进制形式,比方说n = 9 = (1001),那么我们开始累乘底数变量,也就是上面代码中的x。
循环中遇到n的最低位为0的时候,说明这时候的累乘幂x不需要算进最终结果,反之如果遇到n的最低位为1,那么说明这时候的累乘幂x需要算进最终的结果,那么我们需要将Answer与x相乘。
无论如何,每次循环都需要将x与自身相乘,这是因为每次循环我们都将n向右移出一位。
这个算法不仅可以用于整型数字的快速幂计算,也可以计算矩阵乘法等推广形式。爬楼梯这个题就通过斐波那契数列作为中介,引入了某个特殊矩阵的2x2矩阵乘法,随后就可以使用快速幂算法将时间复杂度进一步约简。