汉诺塔函数递归调用问题给个解释

合集下载

汉诺塔的递归算法

汉诺塔的递归算法

汉诺塔的递归算法1. 汉诺塔问题简介汉诺塔是一种经典的递归问题,常用于理解和展示递归算法的思想。

该问题由法国数学家爱德华·卢卡斯于19世纪初提出,得名于印度传说中一个传说故事。

现代汉诺塔问题由3个塔座和一些盘子组成,目标是将所有盘子从一个塔座上移动到另一个塔座上,遵循以下规则:1.一次只能移动一个盘子;2.大盘子不能放在小盘子上面。

2. 汉诺塔问题的递归解法汉诺塔问题的递归解法是一种简洁、优雅且高效的解决方案。

递归算法是一种将大问题划分为更小子问题的方法,通过递归地解决子问题来解决整个问题。

2.1. 基本思想以三个塔座A、B、C为例,假设有n个盘子需要从A移动到C。

递归算法的基本思想如下:1.将n个盘子分成两部分:最底下的一个盘子和上面的n-1个盘子;2.将上面的n-1个盘子从塔座A移动到塔座B,目标塔座为C;3.将最底下的一个盘子从塔座A移动到塔座C;4.将塔座B上的n-1个盘子移动到塔座C,目标塔座为A。

2.2. 递归实现递归解决汉诺塔问题的关键在于理解递归的调用和返回过程。

具体的递归实现如下:def hanoi(n, a, b, c):# n表示盘子的数量,a、b、c表示3个塔座if n == 1:print("Move disk from", a, "to", c)else:hanoi(n-1, a, c, b)print("Move disk from", a, "to", c)hanoi(n-1, b, a, c)# 调用递归函数hanoi(3, 'A', 'B', 'C')上述代码中,当n等于1时,直接将盘子从塔座A移动到塔座C。

否则,递归地将上面的n-1个盘子从塔座A移动到塔座B,然后将最底下的一个盘子从A移动到C,最后再将塔座B上的n-1个盘子移动到塔座C。

汉诺塔问题

汉诺塔问题

汉诺塔问题递归调用的内部执行过程:1.运行开始时,首先为递归调用建立一个工作栈,其结构包括值参,局部变量,和返回地址;2.每次执行递归调用之前,把递归函数的值参和局部变量的当前值及调用后的返回地址入栈;3.每次递归调用结束后,将栈顶元素出栈,使相应的值参和局部变量恢复为调用前的值,然后转向返回地址指定的位置继续执行。

#include <iostream>using namespace std;void Move(char A,char C);void Hanoi(int n,char A,char B,char C);void main(){cout<<"***************汉诺塔问题***************"<<endl;int n;cout<<"输入塔A上原始盘子的数目:";cin>>n;cout<<"^^^^^^^^^^^^^^^^^具体移动过程如下:"<<endl;Hanoi(n,'A','B','C');}void Move(char A,char C){cout<<A<<"--->"<<C<<endl;}void Hanoi(int n,char A,char B,char C){if(n==1)Move(A,C);else{Hanoi(n-1,A,C,B);Move(A,C);Hanoi(n-1,B,A,C);}}。

数据结构汉诺塔递归算法

数据结构汉诺塔递归算法

数据结构汉诺塔递归算法1. 什么是汉诺塔问题汉诺塔(Hanoi)是由法国数学家爱德华·卢卡斯(Édouard Lucas)在19世纪初提出的一个经典数学问题。

问题的描述如下:假设有3个柱子(标记为A、B、C),其中柱子A上有n个不同大小的圆盘,按照从上到下的顺序由小到大放置。

现在要将这n个圆盘按照相同的顺序移动到柱子C 上,期间可以借助柱子B。

在移动时,要遵循以下规则:1.每次只能移动一个圆盘;2.每个圆盘只能放置在比它大的圆盘上面;3.只能借助柱子B进行中转。

汉诺塔问题的目标是找到一种最优策略,使得完成移动所需的步骤最少。

2. 汉诺塔问题的递归解法汉诺塔问题的递归解法非常简洁和优雅。

下面就来详细介绍递归解法的思路和步骤。

2.1. 基本思路我们先来思考一个简化版的问题:将柱子A上的n个圆盘移动到柱子B上。

为了实现这个目标,可以进行如下步骤:1.将A柱上的n-1个圆盘通过借助柱子B移动到柱子C上;2.将A柱上的第n个圆盘直接移动到柱子B上;3.将柱子C上的n-1个圆盘通过借助柱子A移动到柱子B上。

根据上述思路,我们可以发现一个递归的规律:将n个圆盘从A柱移动到B柱,可以分解为两个子问题,即将n-1个圆盘从A柱移动到C柱,和将n-1个圆盘从C柱移动到B柱。

2.2. 递归实现根据以上思路,我们可以编写一个递归函数来实现汉诺塔问题的解决。

def hanoi(n, A, B, C):if n == 1:print(f"Move disk {n} from {A} to {B}")else:hanoi(n-1, A, C, B)print(f"Move disk {n} from {A} to {B}")hanoi(n-1, C, B, A)这个递归函数接受4个参数:n 表示圆盘的数量,A、B、C 表示3根柱子的名称。

当 n 为 1 时,直接将圆盘从 A 移动到 B。

python汉诺塔递归详解(一)

python汉诺塔递归详解(一)

python汉诺塔递归详解(一)Python汉诺塔递归解法引言汉诺塔(Hanoi Tower)是一种数学问题和益智游戏,由法国数学家爱德华·卢卡教授在19世纪初提出。

通过计算机编程解决汉诺塔问题,可以帮助我们更好地理解递归算法在编程中的应用。

问题描述在汉诺塔问题中,有三个柱子和一些圆盘,每个柱子上的圆盘按照从小到大的顺序叠放。

问题的目标是将所有圆盘从一根柱子移动到另一根柱子上,每次只能移动一个圆盘,并且在移动过程中不允许大圆盘放在小圆盘上面。

解法思路我们可以使用递归的方法解决汉诺塔问题。

下面是解决汉诺塔问题的基本步骤:1.将上面n-1个圆盘从A柱移动到B柱。

2.将最大的圆盘从A柱移动到C柱。

3.将B柱上的n-1个圆盘移动到C柱。

通过递归调用这三个步骤,可以将所有的圆盘从A柱移动到C柱。

代码实现以下是使用Python语言实现汉诺塔递归的代码:def hanoi(n, A, B, C):if n == 1:print("Move disk 1 from", A, "to", C) returnhanoi(n-1, A, C, B)print("Move disk", n, "from", A, "to", C) hanoi(n-1, B, A, C)# 测试代码hanoi(3, "A", "B", "C")运行结果运行上述代码,我们可以得到以下输出结果:Move disk 1 from A to CMove disk 2 from A to BMove disk 1 from C to BMove disk 3 from A to CMove disk 1 from B to AMove disk 2 from B to CMove disk 1 from A to C总结通过递归算法,我们可以轻松解决汉诺塔问题。

汉诺塔问题的详解课件

汉诺塔问题的详解课件

04
数据结构与排序
汉诺塔问题也可以用来解释和演示不同的 数据结构和排序算法。
05
06
通过汉诺塔问题,人们可以更好地理解如 堆、栈等数据结构的应用和优劣。
在物理学中的应用
复杂系统与自组织
汉诺塔问题在物理学中常被用来研究复杂系统和自组织现 象。
通过对汉诺塔问题的深入研究,人们可以发现其在物理学 中的一些应用,如量子计算、自旋玻璃等。
人工智能与机器学习
在人工智能和机器学习中,汉诺塔问题可以被用来演示 如何使用不同的算法来解决问题。
06
总结与展望
对汉诺塔问题的总结
汉诺塔问题是一个经典的递归问题,其核心在于将一个复杂的问题分解为若干个简单的子问题来解决 。
通过解决汉诺塔问题,我们可以了解到递归算法在解决复杂问题中的重要性,以及将大问题分解为小问 题的方法。
此外,汉诺塔问题还被广泛应用于数学教育和计算机 科学教育中,成为许多课程和教材中的经典案例之一

02
汉诺塔问题的数学模型
建立数学模型
定义问题的基本参数
盘子的数量、柱子的数量和塔的直径 。
建立数学方程
根据问题的特点,我们可以建立如下 的数学方程。
递归算法原理
递归的基本思想
将一个复杂的问题分解成更小的子问题来解决。
通过深入研究汉诺塔问题的本质和解决方法,我们可以 为解决其他领域的问题提供有益的启示和方法。
THANKS
感谢观看
其他移动规则
除了传统的规则(盘子只能放在更大的盘子下面)之外,还 可以有其他移动规则,这会改变问题的性质和解决方案。
05
汉诺塔问题的应用场景
在计算机科学中的应用
算法设计与优化
01

浅析用递归算法解决汉诺塔问题

浅析用递归算法解决汉诺塔问题

浅析用递归算法解决汉诺塔问题摘要:汉诺塔问题是源于印度一个益智游戏。

在三柱子上按从小到大的顺序摞着64片圆盘。

要求把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。

并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

面对汉诺塔问题我们可以将其想想成一个抽象的数学问题,利用计算机的递归算法对汉诺塔问题进行简单的算法分析求解。

关键词:汉诺塔问题、递归算法1引言汉诺塔是一个发源于印度的益智游戏,也叫河内塔。

相传它源于印度神话中的大梵天创造的三个金刚柱,一根柱子上叠着上下从小到大64个黄金圆盘。

大梵天命令婆罗门将这些圆盘按从小到大的顺序移动到另一根柱子上,其中大圆盘不能放在小圆盘上面。

当这64个圆盘移动完的时候,世界就将毁灭。

而递归算法在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。

递归式方法可以被用于解决很多的计算机科学问题,因此它是计算机科学中十分重要的一个概念。

绝大多数编程语言支持函数的自调用,在这些语言中函数可以通过调用自身来进行递归。

因此递归算法是解决汉诺塔问题的最简单方法。

递归是将重复将问题分解为同类的子问题而解决问题的方法,而汉诺塔问题就是一个不停重复同一个问题的过程。

所以递归算法是解决汉诺塔问题的最合适的方法。

我们可以通过递归算法的思想将汉诺塔问题分成N个问题并且这N个问题与原题目相同且独立,让每一层递归所算的问题都是原问题上的一个小问题,直到每个小问题都解决完毕,也就解决掉了最开始的汉诺塔大问题。

而要实际解决汉诺塔问题我们大致分为:将问题抽象为数字问题;寻找规律,分解问题;列出步骤,算法分析;写出代码完成解题。

首先将汉诺塔问题抽象为数字问题,建立数学模型是为了更好的看清题目,更方便容易的从题目中找到关键点,以此来分解问题,列出式子,再找出其中规律。

我们将抽象的数学问题将分割成n个小问题,然后根据每个小问题的算法,找出所有小问题的共同点,找出规律,再根据每个小问题的解决步骤写出程序算法,然后汇总出最终程序步骤即可。

汉诺塔递归算法及详解

汉诺塔递归算法及详解

汉诺塔递归算法及详解
汉诺塔(Tower of Hanoi)是一个经典的数学谜题和递归问题。

它由三个塔杆和一些不同大小的圆盘组成,开始时圆盘按从大到小的顺序叠放在一个塔杆上。

目标是将所有圆盘从起始塔杆移动到目标塔杆上,同时遵守以下规则:
1. 一次只能移动一个圆盘。

2. 任何时刻,大的圆盘不能放在小的圆盘上面。

递归算法是解决汉诺塔问题的常用方法。

其基本思想是将问题分解为较小规模的子问题,然后通过递归地解决子问题来解决原问题。

以下是汉诺塔递归算法的详解:
1. 如果只有一个圆盘需要移动,则直接将圆盘从起始塔杆移动到目标塔杆上。

2. 如果有多个圆盘需要移动,则按以下步骤进行操作:
- 将除最下方的圆盘以外的上方圆盘从起始塔杆移动到辅助塔杆上。

这可以通过递归调用解决较小规模的子问题来实现,即将上方圆盘从起始塔杆移动到目标塔杆上(目标塔杆作为新的辅助塔杆)。

- 然后将最下方的圆盘从起始塔杆直接移动到目标塔杆上。

- 最后,将辅助塔杆上的所有圆盘移动到目标塔杆上,这可以通过递归调用解决较小规模的子问题来实现,即将上方圆盘从辅助塔杆移动到起始塔杆上(起始塔杆作为新的目标塔杆)。

通过递归地应用以上步骤,就可以实现将所有圆盘从起始塔杆移动到目标塔杆上的操作。

数据结构求解汉诺塔问题的递归算法

数据结构求解汉诺塔问题的递归算法

数据结构求解汉诺塔问题的递归算法汉诺塔问题是一个经典的数学问题,它可以通过递归算法来求解。

在这个问题中,我们需要将一堆盘子从一个柱子移动到另一个柱子,同时遵守以下规则:一次只能移动一个盘子,大盘子不能放在小盘子上面。

为了解决这个问题,我们可以使用数据结构中的栈来模拟柱子的堆叠。

我们可以将每个柱子表示为一个栈,每个盘子表示为一个元素。

初始时,所有的盘子都在第一个柱子上,我们需要将它们移动到第三个柱子上。

下面是求解汉诺塔问题的递归算法的伪代码:```1. 定义一个函数hanoi,接受参数n、起始柱子A、辅助柱子B、目标柱子C2. 如果n等于1,则直接将盘子从A移动到C3. 否则,将n-1个盘子从A移动到B,借助C作为辅助柱子4. 将第n个盘子从A移动到C5. 将n-1个盘子从B移动到C,借助A作为辅助柱子```接下来,我们来详细解释一下这个算法。

首先,我们定义了一个函数hanoi,它接受四个参数:n表示盘子的数量,起始柱子A、辅助柱子B和目标柱子C。

在函数内部,我们首先判断如果n等于1,那么我们直接将盘子从A移动到C即可。

这是递归算法的终止条件。

如果n大于1,我们需要将n-1个盘子从A移动到B,借助C作为辅助柱子。

这一步是通过递归调用hanoi函数来实现的。

在递归调用中,我们将n-1作为新的盘子数量,A作为起始柱子,B作为目标柱子,C作为辅助柱子。

接下来,我们将第n个盘子从A移动到C。

这一步是直接操作的,不需要递归调用。

最后,我们需要将n-1个盘子从B移动到C,借助A作为辅助柱子。

同样地,我们通过递归调用hanoi函数来实现这一步。

在递归调用中,我们将n-1作为新的盘子数量,B作为起始柱子,C作为目标柱子,A作为辅助柱子。

通过这样的递归调用,我们可以将所有的盘子从起始柱子A移动到目标柱子C,同时遵守汉诺塔问题的规则。

总结起来,数据结构中的栈可以很好地模拟汉诺塔问题中的柱子堆叠,而递归算法则可以很好地解决这个问题。

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

汉诺塔函数递归调用问题给个解释
[ 标签:汉诺塔,递归调用,递归]
给答案前先确定你认为你的答案是对的,不要从网上直接复制过来,只想要个详细一步步怎么实现的解释,
『→0←』回答:1 人气:1 提问时间:2009-11-17 20:08
答案
13.5 函数的递归调用(选修)
第4次从洗手间里走出来。

在一周前拟写有关函数的章节时,我就将递归调用的内容放到了最后。

函数递归调用很重要,但它确实不适于初学者在刚刚接触函数的时候学习。

13.5.1 递归和递归的危险
递归调用是解决某类特殊问题的好方法。

但在现实生活中很难找到类似的比照。

有一个广为流传的故事,倒是可以看出点“递归”的样子。

“从前有座山,山里有座庙,庙里有个老和尚,老和尚对小和尚说故事:从前有座山……”。

在讲述故事的过程中,又嵌套讲述了故事本身。

这是上面那个故事的好玩之处。

一个函数可以直接或间接地调用自已,这就叫做“递归调用”。

C,C++语言不允许在函数的内部定义一个子函数,即它无法从函数的结构上实现嵌套,而递归调用的实际上是一种嵌套调用的过程,所以C,C++并不是实现递归调用的最好语言。

但只要我们合理运用,C,C++还是很容易实现递归调用这一语言特性。

先看一个最直接的递归调用:
有一函数F();
void F()
{
F();
}
这个函数和“老和尚讲故事”是否很象?在函数F()内,又调用了函数F()。

这样会造成什么结果?当然也和那个故事一样,没完没了。

所以上面的代码是一段“必死”的程序。

不信你把电脑上该存盘的存盘了,然后建个控制台工程,填入那段代码,在主函数main()里调用F()。

看看结果会怎样?WinNT,2k,XP可能好点,98,ME就不好说了……反正我不负责。

出于“燃烧自己,照亮别人”的理念,我在自已的XP+CB6上试了一把,下面是先后出现的两个报错框:
这是CB6的调试器“侦察”到有重大错误将要发生,提前出来的一个警告。

我点OK,然后无厌无悔地再按下一次F9,程序出现真正的报错框:
这是程序抛出的一个异常,EStackOverflow这么看:E字母表示这是一个错误(Error),Stack正是我们前面讲函数调用过程的“栈”,Overflow意为“溢出”。

整个 StasckOverflow 意思就:栈溢出啦!
“栈溢出”是什么意思你不懂?拿个杯子往里倒水,一直倒,直到杯子满了还倒,水就会从杯子里溢出了。

栈是用来往里“压入”函数的参数或返回值的,当你无限次地,一层嵌套一层地调用函数时,栈内存空间就会不够用,于是发生“栈溢出”。

(必须解释一下,本例中,void F()函数既没有返回值也没有参数,为什么还会发生栈溢出?事实上,调用函数时,需要压入栈中的,不仅仅是二者,还有某些寄存器的值,在术语称为“现场保护”。

正因为C,C++使用了在调用时将一
些关键数值“压入”栈,以后再“弹出”栈来实现函数调用,所以C,C++语言能够实现递归。


这就是我们学习递归函数时,第一个要学会的知识:
逻辑上无法自动停止的递归调用,将引起程序死循环,并且,很快造成栈溢出。

怎样才能让程序在逻辑上实现递归的自动停止呢?这除了要使用到我们前面辛辛苦苦学习的流程控制语句以后,还要掌握递归调用所引起的流程变化。

13.5.2 递归调用背后隐藏的循环流程
递归引起什么流程变化?前面的黑体字已经给出答案:“循环”。

自已调用自已,当然就是一个循环,并且如果不辅于我们前面所学的if...语句来控制什么时候可以继续调用自身,什么时候必须结束,那么这个循环就一定是一个死循环。

如图:
递归调用还可间接形成:比如 A() 调用 B(); B() 又调用 A(); 虽然复杂点,但实质上仍是一个循环流程:
在这个循环之里,函数之间的调用都是系统实现,因此要想“打断”这个循环,我们只有一处“要害”可以下手:在调用会引起递归的函数之前,做一个条件分支判断,如果条件不成立,则不调用该函数。

图中以红点表示。

现在你明白了吗?一个合理的递归函数,一定是一个逻辑上类似于这样的函数定义:
void F()
{
……
if(……) //先判断某个条件是否成立
{
F(); //然后才调用自身
}
……
}
在武侠小说里,知道了敌人的“要害”,就几乎掌握了必胜的机会;然而,“递归调用”并不是我们的敌人。

我们不是要“除掉”它,相反我们利用它。

所以尽管我们知道了它的要害,事情还要解决。

更重要的是要知道:什么时候该打断它的循环?什么时候让它继续循环?
这当然和具体要解决问题有关。

所以这一项能力有赖于大家以后自已在解决问题不断成长。

就像我们前面的讲的流程控制,就那么几章,但大家今后却要拿它们在程序里解决无数的问题。

(有些同学开始合上课本准备下课)程序的各种流程最终目的是要合适地处理数据,而中间数据的变化又将影响流程的走向。

在函数的递归调用过程中,最最重要的数据变化,就是参数。

因此,大多数递归函数,最终依靠参数的变化来决定是否继续。

(另外一个依靠是改变函数外的变量)。

所以我们必要彻底明了参数在递归调用的过程中如何变化。

13.5.3 参数在递归调用过程中的变化
我们将通过一个模拟过程来观察参数的变化。

这里是一个递归函数:
void F(int a)
{
F(a+1);
}
和前面例子有些重要区别,函数F()带了一个参数,并且,在函数体内调用自身时,我们传给它当前参数加1的值,作为新的参数。

红色部分的话你不能简单看过,要看懂。

现在,假设我们在代码中以1为初始参数,第一次调用F():
F(1);
现在,参数是1,依照我们前面“参数传递过程”的知识,我们知道1被“压入”栈,如图:
F()被第1次调用后,马上它就调用了自身,但这时的参数是 a+1,a就是原参数值,为1,所以新参数值应为2。

随着F函数的第二次调用,新参数值也被入栈:
再往下模拟过程一致。

第三次调用F()时,参数变成3,依然被压入栈,然后是第四次……递归背后的循环在一次次地继续,而参数a则在一遍遍的循环中不断变化。

由于本函数仍然没有做结束递归调用的判断,所以最后的最后:栈溢出。

要对这个函数加入结束递归调用的逻辑判断是非常容易的。

假设我们要求参数变到10(不含10)时,就结束,那么代码如:
void F(int a)
{
if( a < 10)
F(a+1);
}
终于有了一个安全的递归调用例子了。

不过它似乎什么也没有做,我们加一句输出代码,然后让它做我们有关递归的第一个实例吧。

13.5.4 一个安全的递归调用函数实例
例六:用递归实现连续输出整数1到9。

//递归调用的函数:
void F(int a)
{
if( a < 10)
{
cout << a;
F(a+1);
}
}
//然后这样调用:
F(1);
完整的代码请见下载的相应例子。

输出将是:
123456789
请大家自行模拟本题函数的调用过程。

13.5.5 递归函数的返回
这里并不是要讲递归函数的返回值。

天气还不是很冷,你能把身上的衣服脱光一下吗?当初你穿衣服时,一定是先穿上最里层的衣服,然后穿上第二层的,再穿上第三层。

现在让你脱衣服,你就得先脱外层,再脱稍里一层,然后才是最内层。

函数的递归调用,和穿衣脱衣类似,不过内外相反而已。

开始调用时,它是外层调内层,内层调更内一层。

等到最内层由于条件不允许,必须结束了,这下可好,最内层结束了,它就会回到稍外一层,稍外一层再结束时,退到再稍外一层,层层退出,直到最外层结束。

如果用调用折线图来表示前例,则为:
本小节不是讲递归函数的返回值,而是讲递归函数的返回次序。

前面听说要脱衣服而跑掉或跑来的同学,可以各回原位了。

做为本小节的一个例子,我只给实例的代码,请大家考虑会是什么输出结果。

考虑并不单指托着腮做思考状(你以为你是大卫?)。

另外,我相信有很多同学有小聪明,他们凭感觉就可以猜出结果。

聪明很好,但千万别因为聪明而在不知不觉中失去动手调试程序的动力。

代码其实只是在上例中再加上一行。

例七:递归函数的返回次序:
//递归调用的函数:
void F(int a)
{
if( a < 10)
{
cout << a;
F(a+1);
cout << a;
}
}
//然后这样调用:
F(1);
完整代码见下载的例子。

相关文档
最新文档