目录
在解决一个规模为n的问题时,如果满足以下条件,我们可以使用递归来解决:
- 问题可以被划分为规模更小的子问题,并且这些子问题具有与原问题相同的解决方法。
- 当我们知道规模更小的子问题(规模为n-1)的解时,我们可以直接计算出规模为n的问题的解。
- 存在一种简单情况,或者说当问题的规模足够小时,我们可以直接求解问题。
一般的递归求解过程如下:
- 验证是否满足简单情况。
- 假设较小规模的问题已经解决,解决当前问题。
上述步骤可以通过数学归纳法来证明。
基础递归问题:
1. 斐波那契数(简单)
1.1 递归求解
重复的子问题——函数头设计
int fib(int n)
子问题在做什么——函数体设计
fib(n - 1) + fib(n - 2)
递归出口
fib(0) = 0
fib(1) = 1
class Solution {
public:
int fib(int n) {
if (n <= 1)
return n;
return fib(n - 1) + fib(n - 2);
}
};
1.2 迭代求解
递归算法在计算时存在着大量的重复计算,执行效率低,n值稍大时非常耗费时间。斐波那契数列用迭代算法更高效。
class Solution {
public:
int fib(int n) {
if (n <= 1)
return n;
int a = 0;
int b = 1;
int c = 0;
for (int i = 2; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return c;
}
};
2. 爬楼梯(简单)
2.1 递归求解
重复的子问题——函数头设计
int climbStairs(int n)
子问题在做什么——函数体设计
如果先走1级台阶,还剩n - 1级台阶,有climbStairs(n - 1)种走法;如果先走2级台阶,还剩n - 2级台阶,有climbStairs(n - 2)种走法。一共的走法:
climbStairs(n - 1) + climbStairs(n - 2)
递归出口
当n == 1时,只有1种走法。
当n == 2时,可以一次走1级台阶,走两次;也可以一次走2级台阶,走一次。所以一共有2种走法。
climbStairs(1) = 1
climbStairs(2) = 2
class Solution {
public:
int climbStairs(int n) {
if (n <= 2)
return n;
return climbStairs(n - 1) + climbStairs(n - 2);
}
};
(But这题在LeetCode上用递归会超时o(´^`)o)
可以看出爬楼梯是斐波那契数的应用。
2.2 迭代求解
class Solution {
public:
int climbStairs(int n) {
if (n <= 2)
return n;
int a = 1;
int b = 2;
int c = 0;
for (int i = 3; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return c;
}
};
3. 汉诺塔问题(简单)
3.1 递归求解
重复的子问题——函数头设计
有三根柱子A、B、C,A柱上有n个盘子,将所有盘子从A柱经B柱全部移到C柱上。
void dfs(int n, vector<int>& A, vector<int>& B, vector<int>& C)
子问题在做什么——函数体设计
该问题可划分成2个自相似问题和1次移动:
- 将n-1个盘子从A柱经C柱全部移到B柱上:dfs(n - 1, A, C, B);
- 将第n个盘子从A柱移到C柱上:C.push_back(A.back()); A.pop_back();
- 将n-1个盘子从B柱经A柱全部移到C柱上:dfs(n - 1, B, A, C);
递归出口
当A柱只剩1个盘子时(即n == 1时),将其从A柱移到C柱上。
C.push_back(A.back());
A.pop_back();
class Solution {
public:
void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
if (A.empty())
return;
dfs(A.size(), A, B, C);
}
private:
// n个盘子从A经B移到C
void dfs(int n, vector<