leetcode原题
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
就是有个n阶的楼梯,你一次可以爬一阶或者两阶,有多少种爬法。
一眼看上去很简单嘛,简单的递归就搞定了:
class Solution {
public:
int climbStairs(int n) {
cl(n);
return sum;
}
void cl(int n)
{
if (n<=0)
{
if (n == 0)
sum++;
return;
}
cl(n - 1);
cl(n - 2);
}
int sum = 0;
}
思路应该不用太多解释,就是没递归一次都分爬一阶、两阶两种情况,爬完就++。真是sometimes naive,果断超时……
第二版:
这一定是递归的问题嘛,把递归去掉就好了。用两个容器,把每次爬的两种情况都记录下来,一个用来记录上次爬完后各种情况剩余的阶数,一个用来记录新的阶数。
这次再有点追求,step不止是2,n步也可以的嘛。
int climbStairs2(int n)
{
vector<int>ways;
vector<int>oldways;
int steps = 2, sum = 0;
oldways.push_back(n);
for (int i = 0; i < n; i++)
{
for (int j = 0; j != oldways.size(); j++)
{
if (oldways[j] > 0)
{
for (int k = 1; k <= steps && k <= oldways[j]; k++)
{
if (oldways[j] - k == 0)
sum++;
else
ways.push_back(oldways[j] - k);
}
}
}
oldways = ways;
ways.clear();
vector<int>().swap(ways);
}
return sum;
}
-,-还是超时啊,能不能再给力一点啊老湿,这都o(n^2),完全没什么效率的提升啊。
第三版:
设n阶的爬法为f(n) ,很显然f(n+1)= f(n) + f(n-1).
因为最后一步只有两种情况,从n阶跨一阶或者从n-1阶跨两阶。
这次效率真提高了,时间复杂度0(n)
int climbStairs3(int n)
{
if (n <= 0)
return 0;
int x0 = 0, x1 = 1;
for (int i = 1; i <= n; i++)
{
int tmp = x1;
x1 += x0;
x0 = tmp;
}
return x1;
}
这次是a掉了,不过说好的n step都支持呢?能不能再给力一点啊老湿?
第四版:
如果最大step为m的话,每次就有m种情况,所以f(n) = f(n-1) + f(n-2) + f(n-3).……+f(n-m).
可以用一个双向队列来保存,这m种情况。每次计算完后,把结果push back,然后pop front把头去掉,就可以吃了。
这里要注意一下当阶数小于step时的情况,
比如当阶数为i, 最大step为maxstep,当前step为j,
当i < j 的时候,自然没有f(i - j)的情况,边界情况为i == j的时候。
所以f(i) == f(j) ,但这时的f (j)除了要加上前面的f (i - j +1) f (i - j + 2) ……f(j-1) 还需要加1, 因为要加上从0直接跨i步到达i阶的2情况。
int climbStairs4(int n)
{
if (n <= 0)
return 0;
const static int maxStep = 2;
int sum = 0;
deque<int>ways(maxStep, 0);
for (int i = 1; i <= n; i++)
{
sum = 0;
for (int j = 1; j <= maxStep && j<= i; j++)
{
if (i>j)
sum += ways[maxStep - j];
if (i == j)
sum++;
}
ways.pop_front();
ways.push_back(sum);
}
return ways.back();
}
PS:这个其实就是费波拉契数列的求和,有直接的求和公式的。还有各种矩阵运算什么的……没有太花心思在上面呢。