CDay06
递归
定义:在函数调用过程中又调用自身的现象
斐波拉契数列
Fibnacci数列:0,1,1,2,3,5,8,13,...
Fn = 0, n = 0 ;
Fn = 1, n = 1 ;
Fn = Fn-2 + Fn-1
long long fib(int n)
{
if(n == 0||n == 1) return 0;
return fib(n-2) + fib(n-1);
}
时间复杂度O(2^n),此方法很耗时,不要采用。
Q:如何避免重复计算问题?
答:顺序求解子问题,这也就可以避免重复计算(动态规划)
最优解:
long long fib(int n)
{
if(n == 0||n == 1) return n;
long long a = 0, b = 1;
for(int i = 2;i <= n;i++)
{
//计算fib(i)的值
long long tmp = a + b;
a = b;
b = tmp;
}
return b;
}
汉诺塔问题
- 边界条件
- 假设会移动 n-1 个盘子,如何移动 n 个盘子(数学归纳法)
递归公式(核心):
- 将 n-1 个盘子移动到 B 上
- 将最大的盘子移动到 C 上
- 将 B 杆上的 n-1 个盘子移动到 C 上
只需关心这一步与递归上一步的关系及边界条件,不要纠结于函数的入栈出栈。
void hanoi(int n, char start, char middle, char target)
{
int count = 0;
//边界条件
if (n == 1)
{
printf("%c --> %c\n", start, target);
return;
}
//递归公式
//把 n-1 个盘子移动到 middle 上
hanoi(n - 1, start, target, middle);
//把最大的盘子移动到 target 上
printf("%c --> %c\n", start, target);
//把 n-1 个盘子移动到 target 上
hanoi(n - 1, middle, start, target);
}
有关移动的次数 s(n)
故:
s(n) + 1 = 2s(n-1) + 2 = 2[s(n-1) + 1],为公比为 2 的等比数列
所以,s(n) = 2^n - 1
总结
1、什么情况下可以考虑使用递归?
递归的结构:
- 递:大问题可以分解成若干个子问题,并且子问题的求解与大问题一致。
- 归:可以将子问题的解合并成大问题的解。
2、使用递归时需要注意的问题?
- 重复计算
- 递归的层次不能太深
3、如何写递归?
- 边界条件
- 递归公式