前言:在有些情况下,我们更倾向于使用递归函数,如排序(合并排序)或树相关操作的算法(heapify/ heapify)。但是,递归函数在某些环境中,如在Visual C ++中,如果调用层次过深,可能会出现如堆栈溢出。许多专业的开发人员可能已经知道如何更改递归功能用迭代函数或使用栈(堆栈)和while循环解决(模拟递归的过程)堆栈溢出问题。
如果您使用的是递归函数,因为你不控制调用堆栈和堆栈大小的限制,当递归调用的深度过深stackoverflow/heap-corruption可能随时发生。如果是换成迭代函数,这个过程还是比较复杂的。但是,为了做到这一点,这里介绍一个十步法将递归程序变换成迭代形式的程序。从而取代使用递归函数的栈,以避免堆栈溢出。下面依次介绍这十条规则。
规则一:1.定义一个结构体,这里取名为:Snapshot.这个结构体主要是用来保存递归程序中每一步用到的数据。
2.该结构体Snapshot应该包含如下信息:
a.递归函数调用过程变化的参数,当该参数为引用的形式,不需要包含在Snapshot结构体中。如下面的函数:void SomeFunc(int n, int &retVal);
参数n须要包括在里面,参数retVal不须要保存。
b.过程保存变量"Stage",通常是一个int型变量来标志当前的阶段。具体看【规则六】
c.调用递归函数返回的局部变量(在二分递归和嵌套递归中会出现)。
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
规则二:1.在迭代函数的顶部定义一个局部变量,用来保存递归函数的返回值。
a.在迭代函数中,该变量是用来保存递归函数中每一次递归调用的返回值
b.如果该递归函数返回值类型为Void, 则可以不定义该局部变量。
c.如果有默认的返回值,则用默认的返回值对该局部变量进行初始化。
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
-
- int retVal = 0;
-
- ...
-
- return retVal;
- }
规则三:创建一个包含Snapshot结构的栈容器。
-
-
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
-
- int retVal = 0;
-
-
- stack<SnapShotStruct> snapshotStack;
- ...
-
- return retVal;
- }
规则四:1.创建一个Snapshot实例,并对里面的参数进行赋初值,然后把它加入到栈容器中。
-
-
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
-
- int retVal = 0;
-
-
- stack<SnapShotStruct> snapshotStack;
-
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
-
- snapshotStack.push(currentSnapshot);
-
- ...
-
- return retVal;
- }
规则五:在函数里面建立一个while循环,当栈容器不为空的时候循环,在循环里面弹出一个Snapshot
-
-
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
- int retVal = 0;
-
- stack<SnapShotStruct> snapshotStack;
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
- snapshotStack.push(currentSnapshot);
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
- ...
- }
-
- return retVal;
- }
规则六: 1.当递归函数里面仅有一处自身调用,则将该过程分为两个阶段;第一阶段是在自身调用之前要处理的事情,第二阶段是在自身调用完之后需要处理的事情。
2.当递归函数有两处自身调用,则需要将该过程分为三个阶段;Stage 1-->递归调用-->(从第一次递归调用返回)Stage 2(第一阶段递归调用)---》(第二次递归调用返回)Stage 3
3.当递归函数有三处自身调用,则需要将该过程分为四个阶段。以此类推。
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
- / Conversion to Iterative Function
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
- int retVal = 0;
-
- stack<SnapShotStruct> snapshotStack;
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
- snapshotStack.push(currentSnapshot);
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
-
- switch( currentSnapshot.stage)
- {
- case 0:
- ...
- break;
- case 1:
- ...
- break;
- }
- }
-
- return retVal;
- }
规则七:根据Snapshot结构体stage的值来分别处理各个过程。
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
-
- int retVal = 0;
-
-
- stack<SnapShotStruct> snapshotStack;
-
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
-
- snapshotStack.push(currentSnapshot);
-
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
-
-
- switch( currentSnapshot.stage)
- {
- case 0:
-
- if( currentSnapshot.n>0 )
- {
- ...
- }
- ...
- break;
- case 1:
-
- currentSnapshot.test = retVal;
- currentSnapshot.test--;
- ...
- break;
- }
- }
-
- return retVal;
- }
规则八:1.如果递归函数中有返回值,则在迭代函数中要用一个局部变量来保存循环迭代的结果。如retVal变量
2.当循环退出时,该局部变量的值就是递归函数最后的结果
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
- int retVal = 0;
-
- stack<SnapShotStruct> snapshotStack;
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
- snapshotStack.push(currentSnapshot);
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
-
- switch( currentSnapshot.stage)
- {
- case 0:
-
- if( currentSnapshot.n>0 )
- {
- ...
- }
- ...
-
- retVal = 0 ;
- ...
- break;
- case 1:
-
- currentSnapshot.test = retVal;
- currentSnapshot.test--;
- ...
-
- retVal = test;
- ...
- break;
- }
- }
-
- return retVal;
- }
规则九:1.在递归函数中,如果有return关键字,在循环中要把它变成continue关键字。
a.如果在递归函数中返回一个值,则用一个局部变量保存该值(如果规则八的retVal),然后再Continue。
b.规则九一般是可选的,主要是为了避免逻辑错误。
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
- int retVal = 0;
-
- stack<SnapShotStruct> snapshotStack;
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
- snapshotStack.push(currentSnapshot);
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
-
- switch( currentSnapshot.stage)
- {
- case 0:
-
- if( currentSnapshot.n>0 )
- {
- ...
- }
- ...
-
- retVal = 0 ;
-
-
- continue;
- break;
- case 1:
-
- currentSnapshot.test = retVal;
- currentSnapshot.test--;
- ...
-
- retVal = test;
-
-
- continue;
- break;
- }
- }
-
- return retVal;
- }
规则十:为了变换递归函数中的递归调用,在迭代函数中新建一个"Snapshot" 对象,并根据递归调用时候的参数来初始 化"Snapshot" 对象的参数。然后加入到容器栈中,加上 Continue. 如果在递归调用之后还有处理语句,更改当
前"Snapshot" 对象的 stage变量的值到相关阶段,在加入新"Snapshot" 对象到容器栈之前,先把当前更新的"Snapshot"对象压入容器栈。
-
- int SomeFunc(int n, int &retIdx)
- {
- ...
- if(n>0)
- {
- int test = SomeFunc(n-1, retIdx);
- test--;
- ...
- return test;
- }
- ...
- return 0;
- }
-
- int SomeFuncLoop(int n, int &retIdx)
- {
-
- struct SnapShotStruct {
- int n;
- int test;
-
-
- int stage;
-
- };
-
- int retVal = 0;
-
- stack<SnapShotStruct> snapshotStack;
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.n= n;
- currentSnapshot.test=0;
- currentSnapshot.stage=0;
- snapshotStack.push(currentSnapshot);
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
-
- switch( currentSnapshot.stage)
- {
- case 0:
-
- if( currentSnapshot.n>0 )
- {
-
- currentSnapshot.stage = 1;
-
- snapshotStack.push(currentSnapshot);
-
-
- SnapShotStruct newSnapshot;
- newSnapshot.n= currentSnapshot.n-1;
-
-
- newSnapshot.test=0;
- newSnapshot.stage=0;
-
-
- snapshotStack.push(newSnapshot);
- continue;
- }
- ...
-
- retVal = 0 ;
-
-
- continue;
- break;
- case 1:
-
- currentSnapshot.test = retVal;
- currentSnapshot.test--;
- ...
-
- retVal = test;
-
- continue;
- break;
- }
- }
-
- return retVal;
- }
一个详细的例子:
- int Fact(long n)
- {
- if(0>n)
- return -1;
- if(0 == n)
- return 1;
- else
- {
- return ( n* Fact(n-1));
- }
- }
-
- int FactLoop(long n)
- {
-
- struct SnapShotStruct
-
- {
- long inputN;
-
- int stage;
- } ;
-
-
- int returnVal;
-
-
- stack<SnapShotStruct> snapshotStack;
-
-
- SnapShotStruct currentSnapshot;
- currentSnapshot.inputN=n;
- currentSnapshot.stage=0;
-
- snapshotStack.push(currentSnapshot);
-
-
- while(!snapshotStack.empty())
- {
- currentSnapshot=snapshotStack.top();
- snapshotStack.pop();
-
-
- switch(currentSnapshot.stage)
- {
-
- case 0:
- if(0>currentSnapshot.inputN)
- {
-
- returnVal = -1;
- continue;
- }
- if(0 == currentSnapshot.inputN)
- {
-
- returnVal = 1;
- continue;
- }
- else
- {
-
-
-
-
-
- currentSnapshot.stage=1;
-
-
- snapshotStack.push(currentSnapshot);
-
-
- SnapShotStruct newSnapshot;
- newSnapshot.inputN= currentSnapshot.inputN -1 ;
-
- newSnapshot.stage = 0 ;
-
- snapshotStack.push(newSnapshot);
- continue;
-
- }
- break;
-
- case 1:
-
-
-
- returnVal = currentSnapshot.inputN * returnVal;
-
-
- continue;
- break;
- }
- }
-
-
- return returnVal;
- }