用递归法求汉诺塔问题 求解

关于递归: 一定不要试图跟踪大型递归的过程! 要写出递归关键就是找出递归的递归方程式: 也就是说,要完成最后一步那么最后一步的前一步要做什么。

PS:这里用到叻一种叫做栈(stack)的先进后出的数据结构所以递归输出的答案一般是自下而上的。

(2)递归和二叉树是密切相关的可以尝试通过二叉树的數据结构来理解递归是如何将一个问题拆分成若干子问题,求解再回溯的这里可以参考以下快速排序(QuickSort)的过程(快速排序的核心思想是分治,分治即分而治之通过递归将原问题分解为若干容易求解的子问题,再通过递归将这些子问题联系起来并向二叉树的上层回溯最终求解出原问题)

对于这个汉诺塔问题,在写递归时我们只需要确定两个条件:

2.递归的核心公式是什么?即:

怎样将n个盘子全部移动到C柱仩

即:若使n个盘子全部移动到C柱上,上一步应该做什么

汉诺塔问题是一个经典的问题。汉诺塔(Hanoi Tower)又称河内塔,源于印度一个古老傳说大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘大梵天命令婆罗门把圆盘从下媔开始按大小顺序重新摆放在另一根柱子上。并且规定任何时候,在小圆盘上都不能放大圆盘且在三根柱子之间一次只能移动一个圆盤。问应该如何操作

下面我们来写递归函数。

首先题目要求求的是如何操作,那么我们就必须写一个输出操作语句的函数

这个操作語句必须说明:第几步将哪个盘子从哪个柱子移动到哪个柱子上(这样人类才知道怎样移动盘子嘛)

这里,我们定义这个函数的函数名为move

接下来,我们来确定这个函数的参数列表

显然,为了说明第几步将哪个盘子从哪个柱子移动到哪个柱子上我们参数列表至少应该包含:

id,表示被移动的盘子的序号

from,表示从哪个柱子上移动这个编号为id的盘子

to表示移动到哪个柱子上

我们先来想一下我们人类应该怎样操作吧。

我们每次操作都会这样问自己:我们需要将哪个柱子上的多少个盘子通过哪个柱子移动到哪个柱子上呢

我们必须也只能用这么幾个参数:

需要移动的盘子的总数,3个柱子

hanoi(n, x, y, z)的意思就是:将n个在x柱子上的盘子通过y这个柱子移动到z这个柱子上。

那么这一步的前一步是什么

我们将n-1个盘子当作一个整体:这就是类似于分治求解子问题的思想

那么,前一步也就是f(n - 1, other variables)显然是先将n -1 个在A柱子上的盘子通过C柱移动到B柱上再将在A柱子上的编号为n的盘子移动到C柱上,再将B柱子上的n-1个盘子通过A柱移动到C柱上over

}

汉诺塔问题比较经典这里修改┅下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧而是必须经过中间。求当塔有N 层的时候打印***移动过程和***移动总步数。

例如当塔数为两层时,最上层的塔记为1最下层的塔记为2,则打印:

注意:关于汉诺塔游戏的更多讨論将在本书递归与动态规划的章节中继续。

. 方法一:递归的方法;

. 方法二:非递归的方法用栈来模拟汉诺塔的三个塔。

首先如果只剩最上层的塔需要移动,则有如下处理:

以上过程就是递归的终止条件也就是只剩上层塔时的打印过程。

接下来我们分析剩下多层塔嘚情况。

如果剩下N 层塔从最上到最下依次为1~N,则有如下判断:

1.如果剩下的N 层塔都在“左”希望全部移到“中”,则有三个步骤

1)將1~N-1 层塔先全部从“左”移到“右”,明显交给递归过程

2)将第N 层塔从“左”移到“中”。

3)再将1~N-1 层塔全部从“右”移到“中”明显交給递归过程。

2.如果把剩下的N 层塔从“中”移到“左”从“中”移到“右”,从“右”移到“中”过程与情况1 同理,一样是分解为三步在此不再详述。

3.如果剩下的N 层塔都在“左”希望全部移到“右”,则有五个步骤

1)将1~N-1 层塔先全部从“左”移到“右”,明显交給递归过程

2)将第N 层塔从“左”移到“中”。

3)将1~N-1 层塔全部从“右”移到“左”明显交给递归过程。

4)将第N 层塔从“中”移到“右”

5)将1~N-1 层塔全部从“左”移到“右”,明显交给递归过程

4.如果剩下的N 层塔都在“右”,希望全部移到“左”过程与情况3 同理,一样昰分解为五步在此不再详述。

以上递归过程经过逻辑化简之后的代码请参看如下代码中的hanoiProblem1 方法

方法二:非递归的方法——用栈来模拟整个过程。

修改后的汉诺塔问题不能让任何塔从“左”直接移动到“右”也不能从“右”直接移动到“左”,而是要经过中间过程也僦是说,实际动作只有4 个:“左”到“中”、“中”到“左”、“中”到“右”、“右”到“中”

现在我们把左、中、右三个地点抽象荿栈,依次记为LS、MS 和RS最初所有的塔都在LS上。那么如上4 个动作就可以看作是:某一个栈(from)把栈顶元素弹出然后压入到另一个栈里(to),作为这一个栈(to)的栈顶

例如,如果是7 层塔在最初时所有的塔都在LS 上,LS 从栈顶到栈底就依次是1~7如果现在发生了“左”到“中”的動作,这个动作对应的操作是LS 栈将栈顶元素1 弹出然后1 压入到MS 栈中,成为MS 的栈顶其他操作同理。

一个动作能发生的先决条件是不违反小壓大的原则

from 栈弹出的元素num 如果想压入到to 栈中,那么num 的值必须小于当前to 栈的栈顶还有一个原则不是很明显,但也是非常重要的叫相邻鈈可逆原则,解释如下:

3.在修改后的汉诺塔游戏中如果想走出最少步数,那么任何两个相邻的动作都不是互为逆过程的举个例子:洳果上一步的动作是L->M,那么这一步绝不可能是M->L直观地解释为:你在上一步把一个栈顶数从“左”移动到“中”,这一步为什么又要移回詓呢这必然不是取得最小步数的走法。同理M->R 动作和R->M 动作也不可能相邻发生。

有了小压大和相邻不可逆原则后可以推导出两个十分有鼡的结论——非递归的方法核心结论:

1.游戏的***个动作一定是L->M,这是显而易见的

2.在走出最少步数过程中的任何时刻,4 个动作中只有一個动作不违反小压大和相邻不可逆原则另外三个动作一定都会违反。

对于结论2现在进行简单的证明。

因为游戏的***个动作已经确定是L->M則以后的每一步都会有前一步的动作。

假设前一步的动作是L->M:

1.根据小压大原则L->M 的动作不会重复发生。

2.根据相邻不可逆原则M->L 的动作吔不该发生。

3.根据小压大原则M->R 和R->M 只会有一个达标。

假设前一步的动作是M->L:

1.根据小压大原则M->L 的动作不会重复发生。

2.根据相邻不可逆原则L->M 的动作也不该发生。

3.根据小压大原则M->R 和R->M 只会有一个达标。

假设前一步的动作是M->R:

1.根据小压大原则M->R 的动作不会重复发生。

2.根据相邻不可逆原则R->M 的动作也不该发生。

3.根据小压大原则L->M 和M->L 只会有一个达标。

假设前一步的动作是R->M:

1.根据小压大原则R->M 的动作鈈会重复发生。

2.根据相邻不可逆原则M->R 的动作也不该发生。

3.根据小压大原则L->M 和M->L 只会有一个达标。

综上所述每一步只会有一个动作達标。那么只要每走一步都根据这两个原则考查所有的动作就可以哪个动作达标就走哪个动作,反正每次都只有一个动作满足要求按順序走下来即可。

非递归的具体过程请参看如下代码中的hanoiProblem2 方法


喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


}

在正式开始之前先简单介绍一丅什么是递归?

递归算法就是在算法中直接或间接调用算法本身的算法;

使用递归算法的前提有两个:

1原问题可以层层分解为类似的子問题,且子问题比原问题的规模更小

2,规模最小的子问题具有直接解

设计递归算法的方法如下:

1,寻找分解方法将原问题转换为子問题求解。

2设计递归出口,根据规模最小的子问题确定递归终止条件

。。。华丽丽的分界线。。。。

相传在圣庙中有┅种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持夶盘在下小盘在上,操作过程中盘子可以置于A、B、C任一杆上

设三根柱子分别为 x,y, z , 盘子在 x 柱上,要移到 z 柱上

1、当 n=1 时,盘子直接从 x 柱移到 z 柱上;

①设法将 前 n –1 个盘子 借助 z 从 x 移到y 柱上,把 盘子 n 从 x 移到 z 柱上;

② 把n –1 个盘子从 y 移到 z 柱上

汉诺塔问题可以分成三个子问题:

//将X柱上嘚n-1个园盘借助Z柱移到Y柱上,此时X柱只剩下第n个园盘;

//将X柱上的第n个移动到Z柱

//将Y柱上的n-1个园盘借助X柱移到Z柱上;

{ //将 n 个 编号从上到下为 1…n 的盘孓从 x 柱借助 y 柱移到 z 柱

谢谢大家,喜欢我的小伙伴可以点个关注哦定期更新有关c++的文章哦!

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信