概念
学习使用递归的前提: 函数理解深入(带参的函数,有返回值的函数,函数的嵌套(就是“自身调用自身”)) 操作时,试验无限递归 递归的终止条件 “回溯时还原现场”保证执行前后程序面对问题的状态是相同的。
递归是程序遍历状态空间的基本方式。
以“原问题”为起点,尝试寻找把状态空间缩小到已知的“问题边界”的路线。
再通过该路线反向回溯的遍历方式,就是递归。
程序在每个步骤应该面对相同种类的问题,这些问题都是原问题的一个子问题,可能仅在规模或者某些限制条件上有所区别,并且能够使用“求解原问题的程序”进行求解。程序在执行变换操作执行三个操作:
- 缩小问题状态空间的规模。程序尝试寻找在 “原问题” 与 “问题边界” 之间的变换路线,并向正在探索的路线上迈进一步。
- 尝试求解规模缩小以后的问题,可能成功,可能失败。
- 如果成功,即找到了规模缩小后的问题的答案,那么将答案扩展到当前问题。如果失败,那么重新回到当前问题,程序可能会继续寻找当前问题的其他变换路线,直到最终确定当前问题无法求解。
在使用枚举算法蛮力搜索问题的整个“状态空间”时,经常需要递归。
枚举算法,就是遍历一遍,逐个举例。
初级的枚举很简单,就是for循环的应用,高级的就很难了,复杂在题目理解和遍历的操作方式
Thinking recursively is natural, but need good amount of training.
void TriangleRev(int x) {
if (x <= 0) return;
for (int i = 1; i <= x; i++)
cout << "*";
cout << endl;
TriangleRev(x - 1);
}
/*
Call TriangleRev(7)
TriangleRev(7) <-- prints 7 stars & new line
TriangleRev(6) <-- prints 6 stars & new line
TriangleRev(5) <-- prints 5 stars & new line
TriangleRev(4) <-- prints 4 stars & new line
TriangleRev(3) <-- prints 3 stars & new line
TriangleRev(2) <-- prints 2 stars & new line
TriangleRev(1) <-- prints 1 star & new line
TriangleRev(0) <-- base case
TriangleRev(1)
TriangleRev(2)
TriangleRev(3)
TriangleRev(4)
TriangleRev(5)
TriangleRev(6)
TriangleRev(7)
The output will be:
*******
******
*****
****
***
**
*
*/
void Triangle(int x) {
if (x <= 0) return;
Triangle(x - 1);
for (int i = 1; i <= x; i++)
cout << "*";
cout << endl;
}
/*
Call Triangle(7)
Triangle(7)
Triangle(6)
Triangle(5)
Triangle(4)
Triangle(3)
Triangle(2)
Triangle(1)
Triangle(0) <-- base case
Triangle(1) <-- prints 1 star & new line
Triangle(2) <-- prints 2 stars & new line
Triangle(3) <-- prints 3 stars & new line
Triangle(4) <-- prints 4 stars & new line
Triangle(5) <-- prints 5 stars & new line
Triangle(6) <-- prints 6 stars & new line
Triangle(7) <-- prints 7 stars & new line
The output will be:
*
**
***
****
*****
******
*******
*/
How to trace recursion
// Factorial(n) = n * Factorial(n-1).
// Factorial(0) = Factorial(1) = 1
int Fact(int n)
{
if(n <= 1)
return 1;
return n * Fact(n-1);
}
/*
Fact(4)
4* Fact(3)
3* Fact(2)
2* Fact(1)
2* 1 <-- 2
3* 2 <-- 6
4* 6 <-- 24
*/
void printNumber(int n) // for n > 0
{ // 1234567
if(n)
{
printNumber(n/10); // 214/10 = 21
cout<<n%10; // 214%10 = 4
}
}
// E.g. 7 = 111 214 = 11010110
// If number%2 get for us last bit in binray representation
// If we could print last bit, we could print
void printNumberBits(int n) // for n > 0
{
if(n)
{
printNumberBits(n/2); // 214/2 = 1101011 last bit removed
cout<<n%2; // 214%2 = 0
}
}
入门例题
爬楼梯
// 理解推导公式后,利用递归实现
菲波那契数列
// 理解推导公式后,利用递归实现
Pell数列
// 需要使用记忆化