1.常使用递归的一些情况:
1.定义是递归的:
eg阶乘函数
这种情况常使用 分治法:把复杂问题分解成几个相对简单且接法相同或类似的子问题 | |
条件是:子问题更简单、处理对象更小、且有边界 | |
一般形式为:
|
2.数据结构是递归的:
eg链表:结点LNode由数据域和指针域组成,而指针域是指向下一个LNode类型的指针
打印链表
//不使用递归 void display(link *p) { link* temp = p;//将temp指针指向头结点 //只要temp指针指向的结点的next不是Null,就执行输出语句。 while (temp->next) { temp = temp->next; printf("%d ", temp->elem); } printf("\n"); } // 使用递归 void display2(link *p){ link* temp = p; if(temp->next==NULL){ printf("\n"); return ; }else{ printf("%d",temp->elem); display2(temp->next); } } // 当if递归条件结束时,只返回空,可以化简 void display3(link *p){ link *temp=p; if(temp){ printf("%d",temp->elem); display2(temp->next); } }
|
3.问题的解法是递归的
Eg
Hanoi塔(汉诺)问题描述: 有n个盘子在A处, 盘子从大到小排列,最上面的盘子最小。 现在要把这n个盘子从A处搬到C处,可以在B处暂存, 但任何时候都不能出现大盘子压在小盘子上面的情况。 |
#include <iostream> using namespace std; void move(int n,char A,char C){ // 把第n个盘子从A移动到C cout<<n<<","<<A<<","<<C<<endl; } // 将1到n个盘子,从A移动C,B作为过渡 void Hanoi(int n,char A,char B,char C){ if(n==1){ // 当n=1时直接一步操作即可 move(1,A,C); }else{ // 当n>=2时的操作具有相似性,都需进行以下3步: // 1,递归,将1到n-1个盘子,从A移动B,C作为过渡 Hanoi(n-1,A,C,B); // 2,将第n个盘子从A移动到C,必须把最大的放在最底下 move(n,A,C); // 3,递归,将1到n-1个盘子,从B移动C,A作为过渡 Hanoi(n-1,B,A,C); } } int main() { int n; cin>>n; char A='A',B='B',C='C'; Hanoi(n,A,B,C); return 0; } |
2.栈与递归
1.在运行一个函数期间调用另一个函数:
在运行被调用函数之前,系统需要完成三件事:
1、将所有的实参、返回地址等信息传递给被调函数保存。
2、为被调函数的局部变量分配存储区。
3、将控制转移到被调函数入口。
而从被调用函数返回调用函数之前,系统也要完成三件事:
1、保存被调函数的计算结果。
2、释放被调函数的数据区。
3、依照被调函数保存的返回地址将控制转移到调用函数。
2.通过栈来解释:
1,当多个函数构成嵌套调用(例如递归函数)时,按照“先调用后返回”的原则
2,上述函数之间信息传递和控制转移必须通过“栈”来实现
3,系统将整个程序的运行空间安排在一个栈中。
每当调用一个函数时,就在栈顶分配一个存储区,(入栈)
每当一个函数退出后,释放它的存储区,(出栈)
所以当前运行的函数的数据区一定在栈顶。
活动记录:函数被调用时分配的存储空间,相当于栈的一个结点 |
Main() | f(4) | f(3) | f(2) | f(1) | f(0) | -> <- |
0,栈底 | 1 | 2 | 3 | 4 | 5,栈顶元素 |
3.递归算法的效率分析
1.时间复杂度
不同问题的时间复杂度不一样
迭代法
递归算法时间复杂度分析_AcceptedLin的博客-CSDN博客_递归的时间复杂度怎么算
eg汉诺塔问题
2.空间复杂度
S(n)=O(f(n)),f(n)为递归工作栈中,工作记录个数与问题规模n的函数关系
4.利用栈将递归转化为非递归
可以利用栈的特点,写出相应的非递归算法
这样做主要是为了防止:当递归次数足够大时,会导致函数栈溢出而死机
PS:很多情况下,代码的易维护性是一个比性能开销更加重要的因素,因此,只要实际应用中不会造成函数栈溢出,可以采用递归函数法。