众所周知,所有递归函数都可以使用单个循环和堆栈编写。虽然这种转换可能会被批评为丑陋或不太可读,但它的主要用途显然是避免粉碎堆。
有一些自然的方法可以将简单的递归函数转换为循环。例如,使用累加器进行简单的尾递归消除。到目前为止,我还没有看到这个问题的明确答案。
至少对我而言,有时将这种递归函数转换为循环提供堆栈似乎是黑魔法。例如,考虑为
编写非递归版本f(n) = n, if n <= 1
f(n) = f(n-1) + f(n-2) + 1, if n > 1
这个问题的基础是:
答案 0 :(得分:3)
可行性(100%):
从here,我们知道任何递归函数都可以转换为迭代(转换为循环),但您需要自己使用堆栈来保持状态。
如何操作(一般):
您可以查看文章How to replace recursive functions using stack and while-loop to avoid the stack-overflow,其中提供了有关如何将递归函数转换为堆栈和while循环的示例和步骤( 10步/规则)。请参阅以下部分以获取真实示例。
示例强>:
以下面的递归函数为例:
// Recursive Function "First rule" example
int SomeFunc(int n, int &retIdx)
{
...
if(n>0)
{
int test = SomeFunc(n-1, retIdx);
test--;
...
return test;
}
...
return 0;
}
应用文章中介绍的10条规则/步骤(评论中显示的详细信息)后,您将获得:
// Conversion to Iterative Function
int SomeFuncLoop(int n, int &retIdx)
{
// (First rule)
struct SnapShotStruct {
int n; // - parameter input
int test; // - local variable that will be used
// after returning from the function call
// - retIdx can be ignored since it is a reference.
int stage; // - Since there is process needed to be done
// after recursive call. (Sixth rule)
};
// (Second rule)
int retVal = 0; // initialize with default returning value
// (Third rule)
stack<SnapShotStruct> snapshotStack;
// (Fourth rule)
SnapShotStruct currentSnapshot;
currentSnapshot.n= n; // set the value as parameter value
currentSnapshot.test=0; // set the value as default value
currentSnapshot.stage=0; // set the value as initial stage
snapshotStack.push(currentSnapshot);
// (Fifth rule)
while(!snapshotStack.empty())
{
currentSnapshot=snapshotStack.top();
snapshotStack.pop();
// (Sixth rule)
switch( currentSnapshot.stage)
{
case 0:
// (Seventh rule)
if( currentSnapshot.n>0 )
{
// (Tenth rule)
currentSnapshot.stage = 1; // - current snapshot need to process after
// returning from the recursive call
snapshotStack.push(currentSnapshot); // - this MUST pushed into stack before
// new snapshot!
// Create a new snapshot for calling itself
SnapShotStruct newSnapshot;
newSnapshot.n= currentSnapshot.n-1; // - give parameter as parameter given
// when calling itself
// ( SomeFunc(n-1, retIdx) )
newSnapshot.test=0; // - set the value as initial value
newSnapshot.stage=0; // - since it will start from the
// beginning of the function,
// give the initial stage
snapshotStack.push(newSnapshot);
continue;
}
...
// (Eighth rule)
retVal = 0 ;
// (Ninth rule)
continue;
break;
case 1:
// (Seventh rule)
currentSnapshot.test = retVal;
currentSnapshot.test--;
...
// (Eighth rule)
retVal = currentSnapshot.test;
// (Ninth rule)
continue;
break;
}
}
// (Second rule)
return retVal;
}
Prize winner in Competition <Best C++ article of July 2012>
,因此它应该是可信任的。 :)
答案 1 :(得分:0)
是的,但解决方案不会比递归解决方案好很多,除非可能减少堆栈溢出的可能性。
通过查看类似于Fibonacci序列的序列,除了你为除n =(0,1)之外的每一轮的结果加1,你会看到一个模式。
0 1 2 4 7 12 20
\ \___ ...\____
0+1+1 1+2+1 7+12+1
由于整个生成依赖于前两个数字,因此在循环中可以有两个变量来代表它,并且根本不需要堆栈。
//intentional generic algol dialect
int modfib (int n)
{
if( n <= 1 )
return n;
n = n - 2;
int a=2;
int b=4;
int tmp;
while( n-- > 0 )
{
tmp=a;
a=b;
b = tmp + b +1;
}
return a;
}
在编译器知道这样做之前,我们仍然需要人来优化代码。