讲义一 递归的消除

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

递归算法具有两个特性:

(1) 递归算法是一种分而治之、把复杂问题分解为简单问题的求解问题方法,对求解某些复杂问题,递归算法分析方法是有效的。

(2)递归算法的时间效率差,其时间效率低。

为此,对求解某些问题时,我们希望用递归算法分析问题,用非递归算法求解具体问题; 消除递归原因:

其一:有利于提高算法时空性能,因为递归执行时需要系统提供隐式栈实现递归,效率低,费时。

其二:无应用递归语句的语言设施环境条件,有些计算机语言不支持递归功能,如FORTRAN 、C 语言中无递归机制 。

其三,递归算法是一次执行完,这在处理有些问题时不合适,也存在一个把递归算法转化为非递归算法的需求。

理解递归机制,是掌握递归程序技能必要前提。消除递归要基于对问题的分析,常用的有两类消除递归方法。

一类是简单递归问题的转换,对于尾递归和单向递归的算法,可用循环结构的算法替代。 另一类是基于栈的方式,即将递归中隐含的栈机制转化为由用户直接控制的明显的栈。利用堆栈保存参数,由于堆栈的后进先出特性吻合递归算法的执行过程,因而可以用非递归算法替代递归算法。

在大量复杂的情况下,递归的问题无法直接转换成循环,需要采用工作栈消除递归。工作栈提供一种控制结构,当递归算法进层时需要将信息保留;当递归算法出层时需要从栈区退出信息。

栈及其应用

一.栈的特点:

栈是一种线性表,对于它所有的插入和删除都限制在表的

同一端进行,这一端叫做栈的“顶”,另一端则叫做栈的“底”,

其操作特点是“后进先出”。

二.栈的抽象数据定义:

1、栈的数组表示 — 顺序栈

s 为栈、p 为指向栈顶的指针

type

stack=record

data:array[1..m] of datatype;

p:0..m

end;

var

s:stack; 2、栈的链接表示 — 链式栈

bottom

当栈的容量无法估计时,可采用链表结构

--链式栈.

链式栈的栈顶在链头.

无栈满问题,空间可扩充.

进栈(插入)与出栈(删除)都在栈顶处执行.

三.栈的基本操作:

(1)进栈操作push(s,x):往栈中推入元素x的项目;

若p=m则write('overflow')

否则p:=p+1;s[p]:=x;

(2)出栈操作pop(s):将栈顶元素中弹出;

若p=0则write('underflow')

否则p:=p-1;

(3)读栈顶元素top(s,x):把栈顶元素的值读到变量x中,栈保持不变;

若p=0则write('error')

否则x:=s[p];

(4)判栈是否为空sempty(s):这是一个布尔函数,当栈sp中没有元素(即t=0)时,称它为空栈,函数取真值,否则值为假。

若p=0则sempty:=true

否则sempty:=false;

(5)链式栈的进栈、出栈操作

进栈:数据元素进栈时,先生成一个新结点P,置数据域为X、指针域指向原栈顶结点,栈顶结点指向P。(在链头插入一个新结点)

出栈:先从栈顶取出数据元素至X,然后把S结点指到它的直接后继结点,原S结点清空。(在链头删去一个结点)

例9、Ackermann函数

[问题描述]

已知Ackermann函数定义如下:

1、手工计算Ack(3,2) 和Ack(3,6)。

解答:29和509

2、写出计算Ack(m,n)的递归算法程序。

program ackermann1;

var m,n:longint;

function ack(m,n:longint):longint;

begin

if m=0

then ack:=n+1

else if n=0

then ack:=ack(m-1,1)

else ack:=ack(m-1,ack(m,n-1))

end;

begin

write('Input m,n:');

readln(m,n);

writeln(ack(m,n))

end.

3、写出计算Ack(m,n)的非递归算法程序。

program ackermann2;

type stack=array [1..8000,1..2] of longint;

var m,n,top:longint;

s:stack;

begin

write('Input m,n:');

readln(m,n);

s[1,1]:=m; s[1,2]:=n;

top:=1;

while top>0 do

begin

m:=s[top,1];

n:=s[top,2];

top:=top-1;

if (top=0) and (m=0) then

begin writeln(n+1); exit end;

if m=0

then s[top,2]:=n+1

else if n=0

then begin top:=top+1; s[top,1]:=m-1; s[top,2]:=1 end

else begin top:=top+1; s[top,1]:=m-1;

top:=top+1; s[top,1]:=m; s[top,2]:=n-1 end

end

end.

下面,我们就以ack(2,1)为例,开始分析递归调用树,采用一个栈记忆每次递归调用时的实参值,每个结点两个域{vm, vn}。对以上实例,递归树以及栈的变化如下:

相关文档
最新文档