递归程序的非递归实现

问题:自己管理栈来实现递归程序。找出通用的改写方法。

 

解答思路:模拟递归程序的执行过程,并每次将递归当前层的调用现场手动压入栈和弹出栈。

 

分析:

举一个简单的例子来分析递归的调用过程,如factor(n) =  n*factor(n-1).

递归程序如下:

int factor(int n)
{
    if(n == 1)
        return 1;
    return n*factor(n-1);
}

其执行过程如下:

clip_image001

由图可知,递归函数每一层的调用现场将被访问两次,并且符合“后进先出”的特点,这也是为什么要用栈。

那么我们分析stage 0这一步都做了些什么:

image

上图说明了在每一层,stage 0时执行的就是递归函数里,递归调用下一层的语句之前的代码。其事实也很明显,不再赘述。同样,stage 1时,由下向上执行的是递归函数里,递归调用下一层的语句之后的代码。

依此分析,将原递归函数分为如下片段:

image

好了,总结下上面的分析:

要将递归转为非递归,首先需要保存每一层的状态信息,包括函数参数,函数返回值等。每一层还有可能处于2个(或者更多,后面举例)不同的stage,这个信息也要记录下来。然后就是依据递归函数的调用位置(也就是入栈时刻)分别处理,在入栈前处理stage 0的代码,入栈后处理stage 1的代码。

 

解答:

现在来写对应的非递归程序:

1.首先定义一个结构体,保存每一层的递归调用现场。

结构体包含:

1)递归函数的参数;

2)如果函数有返回值,也要包括一个返回值类型。

3)一个stage变量,指示当前处于哪个stage。

struct SnapShort{
    int n;
    int retVal;
    int stage;
};

2.定义一个栈,来保存这个结构体。

stack<SnapShort> ss;

3.将初始状态入栈。

SnapShort currentSnap;
currentSnap.n = n;
currentSnap.retVal = 0;
currentSnap.stage = 0;

ss.push(currentSnap);

4.while循环,模拟整个递归过程。

int retVal = 0;//定义一个变量保存函数的最终返回值。
while(!ss.empty())
{
    currentSnap = ss.top();
    ss.pop();
    
    switch(currentSnap.stage)
    {
    case 0:
        //执行递归下一层之前的代码
        if( currentSnap.n == 1)
        {
            retVal = 1;
            continue;
        }    
        currentSnap.stage = 1;
        ss.push(currentSnap);
        
        SnapShort newSnap;
        newSnap.n = currentSnap.n-1;
        newSnap.retVal = 0;
        newSnap.stage = 0;
        
        ss.push(newSnap);
        break;
    case 1:
        //执行递归下一层之后的代码
        currentSnap.retVal = currentSnap.n * retVal;
        
       retVal = currentSnap.retVal;
       break;
    }
}
return retVal;

 

 

一点注释:

在while循环里,首先将当前状态结构体出栈,分析其处于哪个stage。

如果是stage 0,执行递归函数里属于stage 0的代码。然后将当前状态的stage改为1,并入栈。因为当前层已经处理完。然后创建下一层的状态结构体,初始化对应的值,其stage当然为0,入栈。(也就对应着递归调用下一层的语句。)

如果是stage 1,说明递归已经由下向上执行,处理stage 1对应的代码即可。

 

----------------------------------华丽的分割线------------------------------------------

例子2:关于多个stage的情况。

汉诺塔问题:托盘A上有N个从小到大的盘子,请借助B的辅助,将它们全部移动到C上。小盘子不能叠在比它大的盘子上。

image

递归实现很简单:

void hanoi(int n, char a, char b, char c)//初始a为’A', b为‘B’, c为‘C'
{
    if(n == 1)
    {
        cout << a << '->' << c << endl;
        return;
    }
    hanoi(n-1, a, c, b);
    cout << a << '->' << c << endl;
    hanoi(n-1, b, a, c);
}

非递归实现:

由递归程序可以看到,2次递归调用了下一层的自己。所以会有3个stage。如下:

clip_image001[26]

对应的非递归程序如下:

void nonrecursive_hanoi(int n, char a, char b, char c)
{
    struct SnapShort {
        int n;
        char a;
        char b;
        char c;
        int stage;
    };
    
    stack<SnapShort> ss;
    
    SnapShort currentSnap;
    currentSnap.n = n;
    currentSnap.a = a;
    currentSnap.b = b;
    currentSnap.c = c;
    ss.push(currentSnap);

    while(!ss.empty())
    {
        currentSnap = ss.top();
        ss.pop();

        switch(currentSnap.stage)
        {
        case 0:
            if(currentSnap.n == 1)
            {
                cout << currentSnap.a << "->" << currentSnap.c << endl;
                continue;
            }
            currentSnap.stage = 1;
            ss.push(currentSnap);
            
            SnapShort newSnap;
            newSnap.n = currentSnap.n - 1;
            newSnap.a = currentSnap.a;
            newSnap.b = currentSnap.c;
            newSnap.c = currentSnap.b;
            newSnap.stage = 0;
            ss.push(newSnap);
            
            break;
        case 1:
            cout << currentSnap.a << "->" << currentSnap.c << endl;
            
            currentSnap.stage = 2;
            ss.push(currentSnap);
            
            SnapShort newSnap2;
            newSnap2.n = currentSnap.n - 1;
            newSnap2.a = currentSnap.b;
            newSnap2.b = currentSnap.a;
            newSnap2.c = currentSnap.c;
            newSnap2.stage = 0;
            ss.push(newSnap2);
            
            break;
        case 2:
            break;
        }
    }
}

 

总结:

本文递归转化为非递归的思路,实际上就是手动去模拟递归的执行过程。本文将递归程序划为几个stage,从而有套路的写出非递归程序,极大简化了编写非递归程序的步骤。

这儿有几个技巧:

1.如果递归函数有返回值,需要在非递归函数while循环外声明一个返回值类型的变量,用于记录最终的返回值。如果在递归程序中函数的返回值有默认值,则声明时,可用此默认值来初始化此变量。而递归程序中的return语句,就对应了非递归程序中,在while循环里的对应位置,对此变量赋值。

2.递归程序中每一句递归调用语句,对应while循环中,某层状态结构体的stage的变化。

 

备注:

本文的思路很大的借鉴了这篇论文,附链接:

http://www.codeproject.com/Articles/418776/How-to-replace-recursive-functions-using-stack-and#rules

转载于:https://www.cnblogs.com/tasteonbook/archive/2013/03/16/2962470.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值