10.1 斐波那契数列
题目描述
求斐波那契数列的第 n 项,n <= 39。
解题思路
1.递归
下面的递归方法复杂度太高,无法通过;
class Solution {
public:
int Fibonacci(int n) {
if(n < 0)
return 0;
if(n == 0 || n == 1)
return n;
return Fibonacci(n-1) + Fibonacci(n-2);
}
};
2. 动态规划
如果使用递归求解,会重复计算一些子问题。例如,计算 f(4) 需要计算 f(3) 和 f(2),计算 f(3) 需要计算 f(2) 和 f(1),可以看到 f(2) 被重复计算了。
递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。
此时的复杂度为o(N)
class Solution {
public:
int Fibonacci(int n) {
vector<int> tmp;
tmp.reserve(n+1); //此处必须声明容器的大小,不然下面会出现内存非法访问
tmp.push_back(0);
tmp.push_back(1);
for(int i = 2; i <= n; i++)
{
tmp[i] = tmp[i-1] + tmp[i-2];
}
return tmp[n];
}
};
关于声明固定长度的数组:
//use vector
vector<int> tmp;
tmp.reserve(n+1);
//use array
int* tmp = new int[n+1];
3.动态规划的改进
考虑到第 i 项只与第 i-1 和第 i-2 项有关,因此只需要存储前两项的值就能求解第 i 项,从而将空间复杂度由 O(N) 降低为 O(1)。
class Solution {
public:
int Fibonacci(int n) {
if(n <= 1)
return n;
int pre0 = 0, pre1 = 1;
int ret = 0;
for(int i = 2; i <= n; i++)
{
ret = pre0 + pre1;
pre0 = pre1;
pre1 = ret;
}
return ret;
}
};
此外,由于待求解的 n 小于 40,因此可以将前 40 项的结果先进行计算,之后就能以 O(1) 时间复杂度得到第 n 项的值。
10.2 矩形覆盖
题目
我们可以用 21 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 21 的小矩形无重叠地覆盖一个 2*n 的大矩形,总共有多少种方法?
解题思路
当 n 为 1 时,只有一种覆盖方法;
当 n 为 2 时,有两种覆盖方法;
要覆盖 2n 的大矩形,可以先覆盖 21 的矩形,再覆盖 2*(n-1) 的矩形;或者先覆盖 22 的矩形,再覆盖 2(n-2) 的矩形。而覆盖 2*(n-1) 和 2*(n-2) 的矩形可以看成子问题。该问题的递推公式如下:
class Solution {
public:
int rectCover(int number) {
if(number <= 2)
return number;
int pre1 = 1, pre2 = 2;
int ret = 0;
for(int i = 3; i <= number; i++)
{
ret = pre1 + pre2;
pre1 = pre2;
pre2 = ret;
}
return ret;
}
};
10.3 跳台阶
题目
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
解题思路
当 n = 1 时,只有一种跳法;
当 n = 2 时,有两种跳法;
跳 n 阶台阶,可以先跳 1 阶台阶,再跳 n-1 阶台阶;或者先跳 2 阶台阶,再跳 n-2 阶台阶。而 n-1 和 n-2 阶台阶的跳法可以看成子问题,该问题的递推公式为:
代码段
class Solution {
public:
int jumpFloor(int n) {
if (n <= 2)
return n;
int pre2 = 1, pre1 = 2;
int result = 1;
for (int i = 2; i < n; i++) // i是取值到n-1
{
result = pre2 + pre1;
pre2 = pre1;
pre1 = result;
}
return result;
}
};
10.4 变态跳台阶
题目描述
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级… 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
解题思路
动态规划
class Solution {
public:
int jumpFloorII(int number) {
int* array = new int[number];
for(int i = 0; i < number; i++)
{
array[i] = 1;
}
//array.fill(1);
for(int i = 0; i < number; i++)
{
for(int j = 0; j <i; j++)
{
array[i] += array[j];
}
}
return array[number-1];
}
};
数学推导
跳上 n-1 级台阶,可以从 n-2 级跳 1 级上去,也可以从 n-3 级跳 2 级上去…,那么:
f(n-1) = f(n-2) + f(n-3) + … + f(0)
同样,跳上 n 级台阶,可以从 n-1 级跳 1 级上去,也可以从 n-2 级跳 2 级上去… ,那么:
f(n) = f(n-1) + f(n-2) + … + f(0)
综上:
f(n) - f(n-1) = f(n-1)
即:
f(n) = 2*f(n-1)
补充:
c++中:
pow() 函数用来求 x 的 y 次幂(次方),其原型为:
double pow(double x, double y);
class Solution {
public:
int jumpFloorII(int number) {
return (int) pow(2, number - 1);
}
};