递归的实现原理

Posted yangyuliufeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归的实现原理相关的知识,希望对你有一定的参考价值。

需要用到递归的3种情况:
(1)定义是递归的
计算阶乘的递归函数
longFactorial(longn){
    if(n==0) return1;
    elsereturnn*Factorial(n-1);
}
(2)数据结构是递归的
搜索单链表最后一个结点的算法
LinkNode *FindRear(LinkNode *f){
    if(f==NULL) returnNULL;
    elseif(f->link==NULL) returnf;
    elsereturnFindRear(f->link);
}
在单链表中搜索值等于x的结点
voidSearch(LinkNode *f,T& x){
    if(f==NULL) return;
    elseif(f->data==x) returnf;
    elsereturnSearch(f->link,x); 
}
(3)问题的解法是递归的,如汉诺塔问题

递归工作栈
IA-32使用栈来支持过程的嵌套调用。每个过程都有自己的栈区,称为栈帧(stack frame) , 因此,一个栈由若干栈帧组成,每个栈帧用专门的帧指针寄存器EBP指定起始位置,当前栈帧的范围在其和栈指针寄存器ESP指向区域之间。
IA-32规定,寄存器EAX、ECX和EDX是调用者保存寄存器。当过程P调用过程Q时,Q 可以直接使用这三个寄存器,不用将它们的值保存到栈中,这也意味着,如果P在从Q返回后还要用这三个寄存器的话,P应在转到Q之前先保存它们的值,并在从Q返回后先恢复它们的值再使用。寄存器EBX、ESl、EDI是被调用者保存寄存器,Q必须先将它们的值保存到栈中再使用它们,并在返回P之前先恢复它们的值。
每次递归,必定要先push %ebp(把旧栈底地址保存在新栈顶)和mov %esp,%ebp(把旧栈底地址所在的地址——新栈顶地址设置为新栈底)
技术分享图片
如图所示,Q的过程体执行时,入口参数1的地址总是R[ebp]+8,入口参数2的地址总是R[ebp]+12……(在栈中传递的参数若是基本类型,则都被分配4个字节)
与IA-32不同,x86-64最多可有6个整型或指针型参数通过寄存器传递,超过6个入口参数时,后面的通过栈来传递。在栈中传递的参数若是基本类型,则都被分配8个字节。栈中的地址也变为了8个字节。
RAX、R10和R11为调用者保存寄存器。RBX、RBP、R12、R13、R14和R15为被调用者保存寄存器,需要将它们先保存在栈中再使用,最后返回前再恢复其值。
技术分享图片
过程调用中使用的栈机制和寄存器使用约定,使得可以进行过程的嵌套调用和递归调用。
技术分享图片
对于递归过程,用栈将它改为非递归过程,如用栈帮助求解斐波那契函数的非递归算法
struct Node{   //栈结点的类定义
    longn;    //记忆走过的n
    inttag;   //区分左右递归的标志
}
longFibnacci(longn){
    Stack<Node> S ;
    Node *w;
    longsum=0;
    do{
        while(n>1){
            w->n=n;
            w->tag=1;
            S.push(w);
            n--;
        }
        sum=sum+n;
        while(!S.IsEmpty()){
            S.Pop(w);
            if(w->tag==1){    //tag==1表示向左递归
                w->tag=2;     //tag==2表示向右递归
                S.push(w);
                n=w->n-2;
                break;
            }
        }
    }while(!S.IsEmpty());
}
直接用递归法求解斐波那契函数的时间复杂度是O(2^n),因此可改用迭代法
longFibIter(longn){
    if(n<=1) returnn;
    longtwoback=0,oneback=1,Current;
    for(i=2;i<=n;i++){
        Current=twoback+oneback;  //计算Fib(i-2)+Fib(i-1)的值
        twoback=oneback;     //把Fib(i-1)的值保存作为下一次的Fib(i-2)
        oneback=Current;     //把Fib(i)的值保存作为下一次的Fib(i-1)
    }
    returnCurrent;
}
逆向打印数组A[]中数值的递归算法
voidrecfunc(intA[],intn){
    if(n>=0){
        cout<<A[n]<<",";
        n--;
        recfunc(A,n);
    }
}
改用迭代算法
voiditerfunc(intA[],intn){
    while(n>=0){
        count<<A[n]<<",";
        n--;
    }
}

 

 

以上是关于递归的实现原理的主要内容,如果未能解决你的问题,请参考以下文章

六千字快速复习七种常用排序

六千字快速复习七种常用排序

超详细总结基于比较的七大经典 排序 -- 不会的童鞋快进来补习

2021-10-21java:六千字快速复习七种常用排序

C语言实现九大排序算法(建议收藏!)

C语言实现九大排序算法(建议收藏!)