
上述算法简捷明确,可以证明它是正确的。但只看算法的计算步骤,很难理解它的道理,
也很难理解它的设计思想。下面用递归技术来解洪这个问题。当1-1 时,问题比较简单。此
时,只要将编号为 1的圆盘从塔座。 直接移至塔座力上即可。当n1 时,需要利用塔座c作
为辅助塔座。此时要设法将n-1个较小的圆盘依照移动规则从塔座a移至塔座(上,然后将
剩下的最大圆盘从塔座a移至塔座6上,最后设法将1-1 个较小的圆盘依照移动规则从塔座
e移至塔座6上。由此可见,n个圆盘的移动问题就可分解为两次1-1 个圆盘的移动问题,
这又可以递归地用上述方法来做。由此可以设计出解Hlanoi 塔问题的递归算法如下:
void hanoi(int n, int a, int b, int c) {
if (n>0) {
hanoi(n-1, a, c, b);
move(a, b);
hanoi(n-1, c, b, a);
V
其中,hanoi(n,a, b,c)表示将塔座a 上自下而上,由大到小叠放在一起的 n个圆盘依移动规
则移至塔座6上并仍按同样顺序叠放。在移动过程中,以塔座(作为辅助塔座。move(a, b)
表示将塔座a 上编号为口的圆盘移至塔座6上。
算法 hanoi 以递归形式给出,每个圆盘的具体移动方式并不清楚,因此很难用手工移动
来模拟这个算法。然而,这个算法易于理解,也容易证明其正确性,且易于掌握其设计思想。
像hanoi这样的递归算法,在执行时需要多次调用自身。实现这种递归调用的关键是为
算法建立递归调用工作栈。通常,在
一个算法中调用另一算法时,系统需在运行被调用算法
之前先完成三件事:① 将所有实参指针、返回地址等信息传递给被调用算法:② 为被调用
算法的局部变量分配存储区;③ 将控制转移到被调用算法的入口。
在从被调用算法返回调用算法时,系统相应地要完成三件事:① 保存被调用算法的计
算结果:② 释放分配给被调用算法的数据区:③ 依照被调用算法保存的返回地址将控制转
移到调用算法。
当有多个算法构成嵌套调用时,按照“后调用先返回”的原则进行。上述算法之间的信
息传递和控制转移必须通过栈来实现,即系统将整个程序运行时所需的数据空间安排在一个
栈中,每调用一个算法,就为它在栈顶分配
一个存储区,每退出一个算法,就释放它在栈顶
的存储区。当前正在运行的算法的数据
一定在栈顶。
递归算法的实现类似于多个算法的嵌套调用,只是调用算法和被调用算法是同一个算
法。因此,与每次调用相关的一个重要概念是递归算法的调用层次。若调用一个递归算法的
主算法为第。层算法,则从主算法调用递归算法为进入第1层调用;从第i层递归调用本算
法为进入第 计1层调用。反之,退出第i层递归调用,则返回至第之-1层调用。为了保证递
归调用正确执行,系统要建立一个递归调用工作栈,为各层次的调用分配数据存储区。每层
递归调用所需的信息构成
一个工作记录,其中包括所有实参指针、所有局部变量以及返回上
一层的地址。每进入一层递归调用,就产生
一个新的工作记录压入栈顶。每退出一层递归调
用,就从栈顶弹出一个工作记录。
图 22足实现算法;递归调用的栈使用情况示意,其中TOP 是指向栈顶的指针。由于递
归算法结构消晰,可读性强,且容易用数学归纳法证明算法的正确性,因此它为设计算法
调试程序带米很大方便。然而,递归箄法的运行效瓷
主算法栈块
我低,无论是耗费的计算时间还是占用的存储空间都
比非遊归算法要至。若在程序中消除算法的递归调
用,则其运行时问可大为节省。因此,有时希望在递
主算法调用逆归算法 A 的栈块
第法^ 的第1层逆归调用工作记录
算法A 的第2层逆归调用工作记录
TOP
归算法中消除遊归调用,使其转化为一个非递归算
法。通常,消除递归采用一个用户定义的栈米模拟系
统的递归调用工作栈,从而达到将遊归算法改为非递
归算法的目的。仅仅是机械地模拟还不能达到缩短计
算时间和减小存储空间的目的,还需要根据具体程序
图 2-2
递归调用工作栈示意
的特点对递归调用工作栈进行简化,尽量减少栈操
作,压缩栈存储空间以达到节省计算时间和存储空问的目的。
反思:
说实在根本要我自己想我第一次肯定想不出来。a
学到了什么:
简化的思想,对于复杂的问题我们可以通过分析其特殊情况来得到自己想要的