递归
若在一个函数、过程或数据结构定义的内部又直接(或间接)出现定义本身的应用,则称它们是递归。
- 三种常使用递归的情况:
- 定义是递归的
例1:阶乘函数
long Fact(long n)
{
if (n==0) return 1; //递归终止的条件
else return n*Fact(n-1); //递归步骤
}
例2:Fibonacci数列
long Fib(long n)
{
if(n==1||n==2) return 1; //递归终止的条件
else return Fib(n-1) + Fib(n-2); //递归步骤
}
分解-求解的策略称为分治法
采用分治法进行递归求解问题需要满足的条件:
(1)能将一个问题转变成一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,并且这些处理对象更小且变化有规律
(2)可以通过上述转化而使问题简化
(3)必须有一个明确的递归出口,或称递归的边界
分治法:
void p(参数表)
{
if(递归结束条件成立)可直接求解; //递归终止条件
else p(较小的参数); //递归步骤
}
- 数据结构是递归的
链表是一种递归的数据结构,因为其结点LNode的定义由数据域data和指针域next组成,而指针域next是一种指向LNode类型的指针,即LNode的定义中又用到了其自身。
广义表,二叉树等也是典型的具有递归特性的数据结构。
在递归算法中,当递归结束条件条件成立只执行return操作时,分治法求递归问题的算法可简化为:
void p(参数表)
{
if(递归结束条件不成立)
p(较小的参数);
}
例:遍历输出链表中各个结点的递归算法
void TraverseList(LinkList p)
{
if(p==NULL) return; //递归终止
else
{
cout<<p->data<<endl; //输出当前结点的数据域
TraverseList(p->next); //p指向后继结点继续递归
}
}
void TraverseList(LinkList p)
{
if(p)
{
cout<<p->data<<endl;
TraverseList(p->next);
}
}
- 问题的解法是递归的
一些问题本身没有明显的递归结构,但用递归求解比迭代求解更简单,
例如Hanoi塔问题、八皇后问题、迷宫问题
等。
例:n阶Hanoi塔问题
int m=0;
void move(char A,int n,char C)
{
cout<<++m<<","<<n<<","<<A<<","<<C<<endl;
}
void Hanoi(int n,char A,char B,char C)
{ //将塔座A上的n个圆盘按规则搬到C上,B做辅助他
塔
if(n==1) move(A,1,C); //将编号为1的圆盘从A移到C
else
{
Hanoi(n-1,A,C,B); //将A上编号为1至n-1的圆盘移到B,C做辅助塔
move(A,n,C); //将编号为n的圆盘从A移到C
Hanoi(n-1,B,A,C); //将B上编号为1至n-1的圆盘移到C,A做辅助塔
}
}
函数调用
-
一个函数运行期间调用另一个函数时,在运行被调用函数之前
,系统需先完成:
(1)将所有的实参、返回地址等信息传递给调用函数保存
(2)为被调用函数的局部变量分配存储区
(3)将控制转移到被调函数的入口 -
从被调函数返回调用函数之前
,系统需先完成:
(1)保存被调函数的计算结果
(2)释放被调函数的数据域
(3)依照被调函数保存的返回地址将控制转移到调用函数
递归工作栈
- 求解 4!活动记录进栈过程
- 求解 4!活动记录出栈过程
递归算法的效率分析
1: 时间复杂度的分析
longFact(long n)
{
long temp;
if(n==0) return 1; //活动记录退栈(1)
else temp=n*Fact(n-1); //活动记录进栈(2)
return temp; //活动记录退栈(3)
}
设Fact(n)的执行时间是T(n)。此递归函数中if(n==0) return 1;
的时间复杂度为O(l),递归调用Fact(n-1)的执行时间是T(n-1),所以else temp=n*Fact(n-1);
的执行时间是O(l)+T(n-1)。设两数相乘和赋值的时间复杂度都是O(l),则对某常数C、D有如下递归方程
设n>2,利用上式对T(n-1)展开,即在上式中用n-1代替n得到
T(n-1)=C+T(n-2)
再代入T(n)=C+T(n-1)中,有
T(n)=2C+T(n-2)
同理,n>3时有
T(n)=3C+T(n-3)
以此类推,当n>i时有
T(n)=iC+T(n-i)
最后,当i=n时有
T(n)=nC+T(0)=nC+D
求得递归方程的解为:
T(n)=O(n)
采用这种方法计算Fibonacci数列和Hanoi塔问题递归算法的时间复杂度为O(2^n)
2:空间复杂度的分析
递归函数在执行时,系统会建立一个“递归工作栈”存储每一层递归所需的信息,此工作栈是递归函数执行的辅助空间,因此,分析递归算法的空间复杂度需要分析工作栈的大小。
对于递归算法,空间复杂度
S(n)=O(f(n))
其中,f(n)为“递归工作栈”中工作记录的个数与问题规模n的函数关系。
由此,前面讨论的阶乘问题、Fibonacci数列问题、Hanoi塔问题的递归算法的空间复杂度为O(n)。
递归转换为非递归
- 利用栈消除递归过程的步骤:
(1)设置一个工作栈存放递归工作记录(包括实参,返回地址及局部变量等)
(2)进入非递归调用入口(即被调用程序开始处)将调用程序传来的实在参数和返回地址入栈(递归程序不可以作为主程序,因而可认为初始是被某个调用函数调用
)
(3)进入递归调用入口:当不满足递归结束条件时,递层递归,将实参、返回地址及局部变量入栈,此过程可用循环语句实现—模拟递归分解的过程
(4)递归结束条件满足,将到达递归出口的给定常数作为当前的函数值
(5)返回处理:在栈不空的情况下,反复退出栈顶记录,根据记录中的返回地址进行题意规定的操作,即逐层计算当前函数值,直至栈空为止—模拟递归求值过程
递归算法优点
结构清晰,程序易读,正确性容易得到证明
来源:CSDN
作者:听目A
链接:https://blog.csdn.net/qq_45947684/article/details/104296705