基本思路:将递归过程中出现递归调用的地方,用等价的非递归代码来代替,并对return语句做适当处理。
13条规则:处理直接递归调用中的递归代码和return语句,将之转化成等价的迭代代码。
初始
- 在开始,插入说明为栈的代码并将其初始化为空。在一般情况下,这个栈用来存放参数、局部变量和函数的值、每次递归调用的返回地址。
- 将标号L1附于第一条可执行语句。然后对于每一处递归调用都用一组下列规则的指令来代替。
递归调用语句 - 将所有参数和局部变量的值存入栈。栈顶指针可作为一个全程变量来看待。
- 简历第i个新标号Li,并将i存入栈。这个标号的i值将用来计算返回地址。
此标号放在规则7所描述的程序段中 - 计算这次调用的各实在参数的值,并把这些值赋给相应的形式参数。
- 插入一条无条件转向语句转向过程的开始部分:Goto L1
- 如果这过程是函数,则对递归过程中含有此次函数调用的那条语句做如下处理:将该语句的此次函数调用部分用从栈顶取回该函数值的代码来代替,其余部分的代码按照原描述方式照抄,并将4中建立的标号附于这条语句上。如果不是函数,也将4中建立的标号附于6所产生的转移语句后面的语句。
return处理 - 如果栈为空,则执行正常返回
- 否则,将所有输出参数(带有返回值的出口参数,out/inout型)的当前值赋给栈顶上的那些对应的变量。
- 如果栈中有返回地址标号的下标,就插入一条此下标 从栈中推出的代码,并把这个下标赋给一个未使用的变量。
- 从栈中退出所有局部变量和参数的值并把他们赋给对应的变量。
- 如果这个过程是函数,则插入一下命令,这些指令用来计算紧接在return后面的表达式并将结果值存入栈顶。
- 用返回地址标号的下标实现对该标号的转向。
示例:
求数组元素中的最大值
算法1.10 递归求取数组元素的最大值
procedure MAX1(i)
// 查找数组A中最大值元素,并返回该元素的最大下标。//
global integer n,A(1:n),j,k
integer i
if i<n then j←MAX1(i+1) //递归调用//
if A(i) > A(j) then k←i
else k←j
endif
else k←n
endif
return(k) //递归调用的返回//
end MAX1
消去上例中的递归:
procedure MAX2(i)
local integer j,k;global integer n,A(1:n)
integer I
integer STACK(1:2*n)
top<-0 //规则1,声明栈的代码,并初始化为空
L1:if i<n //规则2,将标号L1赋予第一条可执行代码语句前
then top<-top+1;STACK(top)<-i;//规则3,参数或局部变量值入栈
top<-top+1;STACK(top)<-2;//规则4,建立新的标号2,并入栈
i<-i+1 //规则5,计算参数值
goto L1 //规则6,无条件转向算法的开始部分
L2:j<-STACK(top);top<-top-1;//规则7,处理函数调用,并将标号2赋于该语句上
if A(i)>A(j) then k<-I
else k<-j
endif
else k<-n
endif
if top=0 then return(k)//规则8,如果栈空,则正常返回
else addr<-STACK(top);top<-top-1;//规则10,从栈中退出返回标号
i<-STACK(top);top<-top-1;//规则11,从栈中退出局部变量和参数的值
top<-top+1;STACK(top)<-k;//规则12.计算返回值,并将之入栈
if addr=2 then goto L2 endif//规则12,用返回地址标号的下标实现对该标号的转向
endif
end MAX2