递归
什么是递归?
在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。
所谓递归,简单点来说,就是一个函数直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
我们可以把” 递归 “比喻成 “查字典 “,当你查一个词,发现这个词的解释中某个词仍然不懂,于是你开始查这第二个词。
可惜,第二个词里仍然有不懂的词,于是查第三个词,这样查下去,直到有一个词的解释是你完全能看懂的,那么递归走到了尽头,然后你开始后退,逐个明白之前查过的每一个词,最终,你明白了最开始那个词的意思。
(递归就是有去(递去)有回(归来))
递归需要:
边界条件
递归前进段
递归返回段
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
递归关键:在于找出递归定义和递归终止条件。
递归定义:使问题向边界条件转化的规则。递归定义必须能使问题越来越简单。
递归终止条件:也就是所描述的问题的最简单情况,它本身不再使用递归的定义。
递归调用的内部执行过程如下:
(1)运动开始时,首先为递归调用建立一个工作栈,其结构包括值参、局部变量和返回地址;
(2)每次执行递归调用之前,把递归函数的值参和局部变量的当前值以及调用后的返回地址压栈;
(3)每次递归调用结束后,将栈顶元素出栈,使相应的值参和局部变量恢复为调用前的值,然后转向返回地址指定的位置继续执行。
递归函数特点:
1. 每一级函数调用时都有自己的变量,但是函数代码并不会得到复制,如计算5的阶乘时每递推一次变量都不同;
2. 每次调用都会有一次返回,如计算5的阶乘时每递推一次都返回进行下一次;
3. 递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序;
4. 递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反;
5. 递归函数中必须有终止语句。
递归问题:
递归算法解题相对常用的算法如普通循环等,运行效率较低。
因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。
在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
例子:
#include<iostream>
using namespace std;
int add(int n)
{
cout<<n<<":add start"<<endl; //递归调用前 顺序执行
if (n == 1) { //递归终止条件
return 1;
} else {
return add(n-1) + n; //递归前进段
}
}
int main(int argc, char *argv[])
{
cout<<add(5);
return 0;
}
打印结果:
5:add start
4:add start
3:add start
2:add start
1:add start
15
图解:
变形:
#include<iostream>
using namespace std;
int add(int n)
{
cout<<n<<":add start"<<endl;
if (n == 1) {
return 1;
} else {
add(n-1);
cout<<n<<":add end"<<endl;
return add(n-1) + n;
}
}
int main(int argc, char *argv[])
{
cout<<add(3);
return 0;
}
打印结果:
3:add start
2:add start
1:add start
2:add end
1:add start
3:add end
2:add start
1:add start
2:add end
1:add start
6
递归典型问题: 梵塔问题(汉诺塔问题)
已知有三根针分别用A, B, C表示,在A中从上到下依次放n个从小到大的盘子,现要求把所有的盘子
从A针全部移到B针,移动规则是:可以使用C临时存放盘子,每次只能移动一块盘子,而且每根针上
不能出现大盘压小盘,找出移动次数最小的方案.
// n为盘子数,x为初始柱, y为辅助柱, z为目标柱。
int c = 0;
void hanoi(int n, char x, char y, char z)
{
if (1 == n) {
cout<<++c<<"Move disk"<<n<<"from "<<x<<"to "<<z<<endl;
} else {
hanoi(n-1, x, z, y);
cout<<++c<<"Move disk"<<n<<"from "<<x<<"to "<<z<<endl;
hanoi(n-1, y, x, z);
}
}
int main(int argc, char *argv[])
{
hanoi(3, 'x', 'y', 'z');
return 0;
}