算法:图解汉诺塔问题(递归求解)

汉诺塔:汉诺塔(Tower of Hanoi)源于印度传说中,大梵天创造世界时造了三根金钢石柱子,其中一根柱子自底向上叠着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

下面来对汉诺塔问题进行解释和建立模型
在这里插入图片描述
这是示意图,a是起始柱,c是目标柱,b起到中转作用

在进行转移操作时,都必须确保大盘在小盘下面,且每次只能移动一个圆盘,最终c柱上有所有的盘子且也是从上到下按从小到大的顺序。

下面我用图来描述64个盘子的转移流程
在这里插入图片描述
在这里插入图片描述
这里我们先把上方的63个盘子看成整体,这下就等于只有两个盘子,自然很容易了,我们只要完成两个盘子的转移就行了,好了现在我们先不管第64个盘子,假设a柱只有63个盘子,与之前一样的解决方式,前62个盘子先完成移动目标。

嗯,就这样一步步向前找到可以直接移动的盘子,62,61,60,…,2,1,最终,最上方的盘子是可以直接移动到c柱的,那就好办了,我们的2号盘也能完成向c柱的转移,这时c柱上时已经转移成功的2个盘,于是3号盘也可以了,一直到第64号盘。

package main

func move(id int, from string, to string){
	println("num =", id, ":", from , "->", to)
}

func hanrou(n int, a, b, c string){
	if(n == 0){
		return
	}

	if(n == 1){
		move(1, a, c);
	}else{
		hanrou(n - 1, a, c, b);
		move(1, a, c);
		hanrou(n - 1,b, a, c);
	}
}



func main() {
	hanrou(3, "a", "b", "c")
}

如果真要解释代码的每一步执行过程,那会很乱,两层递归,最后自己也会被绕的晕头转向,这个时候只要理解递归最终的解决的问题是什么就行了,中间的事交给程序,递归可以很绕也可以很直接,我们按照最直接的理解就行了。

最后关于递归算法,这中解决问题的方法也只有计算机才喜欢,我们虽然看着代码很简单,但真要深入理解也是很费脑细胞的,不过递归确实有中数学上的简洁美和逻辑美。

可视化过程

package main

import (
	"fmt"
)

// 柱子高度
const N int = 10
// 三根柱子的取值
const A string = "A"
const B string = "B"
const C string = "C"


var  arr [3][N]int =[3][N]int{{0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0}}

func show()  {
	fmt.Printf("%5s%5s%5s\n", "A", "B", "C")
	for i := 0; i < N ;  i++ {
		for j := 0; j < 3 ; j++ {
			fmt.Printf("%5d", arr[j][i])
		}
		fmt.Println()
	}

	fmt.Println("---------------------")
}

/*
* 初始化时有多少个盘子在A上
*/
func newHanrouta(n int) {
	for i := 0; i < n ; i++ {
		arr[0][N - 1 - i] = n - i
	}
}

// move 作用:将x柱子的最上面的那个移动到Y柱子上
// X, Y 仅仅可以取值 A, B, C
func move (X,Y string){  
	// 将X/Y/Z转换为0,1,2。表示arr[0], arr[1], arr[2]
	m := int(X[0]) - 'A'
	n := int(Y[0]) - 'A'

	// 找到X柱子上第一个不为0的数字(找到第一个需要移动的盘子), 结束循环
	imove := -1
	for i := 0; i< N ; i++ {
		if arr[m][i] != 0 {
			imove = i
			break
		}
	}

	if imove == -1 {
		fmt.Println(X , "柱上已经没有盘子了")
		return
	}


	// 从最下面开始数,直到找到第一个0(找到第一个空闲位置), 结束循环
	jmove := -1
	for i := N - 1; i > -1 ; i-- {
		if arr[n][i] == 0 {
			jmove = i
			break
		}
	}



	if jmove == -1 {
		fmt.Println(Y , "柱上已经没有位置来存放盘子了")
		return
	}

	// imove与jmove交换位置
	arr[m][imove], arr[n][jmove] = arr[n][jmove], arr[m][imove]
}

func hanrou(n int, A, B, C string)  {
	if (n <= 0){
		return
	}

	if n == 1 {
		move(A, C)
		show()
	}else{
		hanrou(n-1, A, C, B)
		move(A, C)
		show()
		hanrou(n-1, B, A, C)
	}
}

func main() {
	newHanrouta(9)
	show()
	hanrou(9, A, B ,C)
	show()
}





### 汉诺塔递归算法流程图图解 #### 什么是汉诺塔问题汉诺塔问题是经典的递归问题之一,其目标是从一根柱子上按照特定规则将盘子移动到另一根柱子上。具体规则如下: 1. 每次只能移动一个盘子。 2. 移动过程中大盘子不能放在小盘子上面。 为了实现这一过程,通常采用递归的方法来解决该问题[^1]。 --- #### 汉诺塔递归算法的核心逻辑 递归的本质在于函数自身的调用,并且每次调用都会缩小问题规模直到达到基本情况(base case)。对于汉诺塔问题来说,核心逻辑可以描述为以下三步: 1. 将 \(n-1\) 个盘子从源柱子借助辅助柱子移动到中间柱子。 2. 将第 \(n\) 个盘子直接从源柱子移动到目标柱子。 3. 再将 \(n-1\) 个盘子从中间柱子借助源柱子移动到目标柱子。 这种分治的思想使得复杂的问题被分解成更小的部分逐步解决[^2]。 --- #### 汉诺塔递归算法的伪代码表示 以下是汉诺塔递归算法的伪代码形式: ```plaintext function Hanoi(n, source, auxiliary, target): if n == 1: move disk from source to target else: Hanoi(n-1, source, target, auxiliary) move nth disk from source to target Hanoi(n-1, auxiliary, source, target) ``` 上述伪代码清晰展示了如何通过递归来解决问题:当只有一个盘子时直接完成操作;否则先处理较小部分,再处理当前层的操作,最后继续处理剩余的小部分[^3]。 --- #### 汉诺塔递归算法的Python实现及其流程图解释 下面是基于 Python 的汉诺塔递归算法实现以及对应的流程图说明: ```python def hanoi(n, source='A', auxiliary='B', target='C'): if n == 1: print(f"Move disk 1 from {source} to {target}") else: hanoi(n-1, source, target, auxiliary) # Step 1: Move (n-1) disks from A to B using C as intermediate. print(f"Move disk {n} from {source} to {target}") # Step 2: Move the largest disk directly. hanoi(n-1, auxiliary, source, target) # Step 3: Move (n-1) disks from B to C using A as intermediate. hanoi(4) # Example call with 4 disks and default poles names 'A', 'B' and 'C'. ``` ##### 对应的流程图解读 假设我们有四个盘子 (\(n=4\)) 并分别标记三个柱子为 `A`(源)、`B`(辅助)和 `C`(目标),则完整的执行路径可以用下述方式展示: 1. **初始状态**: 所有盘子都在 `A` 上。 2. 调用 `Hanoi(4, A, B, C)` 开始递归。 - 首先进入第一步,即调用 `Hanoi(3, A, C, B)` 来把前三个盘子移到辅助柱子 `B`。 - 这一阶段会进一步拆分为多个子步骤直至到达基本情形 (`n==1`)。 - 接着第二步,打印 “Move disk 4 from A to C”,即将最大的盘子移至最终位置。 - 最后进入第三步,再次调用 `Hanoi(3, B, A, C)` 把之前留在辅助柱上的盘子全部转移到目标柱 `C`。 每一步都严格遵循递归定义中的分工合作原则,从而保证整体任务顺利完成。 --- #### 黄金法则——递归终止条件的重要性 无论何时设计递归解决方案都需要特别注意设置合理的退出条件以免陷入无限循环之中。就本案例而言,“如果只剩下一个圆盘,则无需考虑其他情况只需将其转移即可”的设定正是保障程序正常运行的关键所在。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值