《汉诺塔问题递归解法》

问题如下:
假设一共有n个盘子,由1号盘到n号盘依次增大。
记作P1,P2,P3,P4......Pn-1,Pn。

其中P1最小,Pn最大
一共三根柱子;A(start)柱子,B(middle)柱子,C(end)柱子。
注意A,B,C后面的括号里的内容,start代表初始放置柱子,middle代表中转柱子,end代表目标柱子。
问题开始时,start柱子上n个盘子从上到下按从小到大的顺序排列。
middle柱子和end柱子上没有圆盘。
当问题结束时,start柱子和middle柱子上没有圆盘,而end柱子上n个盘子从上到下按从小到大的顺序排列。


---------------------------------------------------------------------------------------
问题的解法一共三大步骤:
步骤1:将A(start)柱子上以某种方法全部放到B(middle)柱子上(此时A(start)柱子只有,middle柱子上有,而C(end)柱子上没有盘子)
步骤2:将A(start)柱子上仅存的挪到空无一物的C(end)柱子上。
步骤3:将B(middle)柱子上剩余的全部挪到C(end)柱子上。
---------------------------------------------------------------------------------------


就是这三步。第二步最好理解,
因为如果不全在B(middle)柱子上的话,也就无法从A(start)柱子挪动到C(end)柱子上。
同时既然都在B(middle)柱子上,那么他们的排列顺序一定是从上到下从小到大。
---------------------------------------------------------------------------------------


下面详解步骤1和步骤3
对于步骤1:完全可以看作是将n-1个盘子由A(start)盘子经由C(middle)盘子挪动到B(end)盘子上。
A仍是初始放置柱子,但此时对于这n-1个盘子,中转柱子B变成了目标柱子B(end),而C柱子成为了中转柱子。
对于步骤3:此时前n-1个盘子都在B柱子上,要将他们挪动到C(end)柱子上,此时B柱子对于这n-1个盘子是初始位置盘,是B(start)。而A柱子成为了中转柱子,是A(middle)。
所以明白了吧?
步骤1和步骤3都分别是一次独立的汉诺塔问题,区别在于:
常规汉诺塔问题:n个盘子,从A(start)柱子,经由B(middle)柱子,到C(end)柱子。
步骤1的汉诺塔问题:n-1个盘子,从A(start)柱子,经由C(middle)柱子,到B(end)柱子。
步骤3的汉诺塔问题:n-1个盘子,从B(start)柱子,经由A(middle)柱子,到C(end)柱子。
---------------------------------------------------------------------------------------


而步骤1又可以变成:
步骤1.1:n-2个盘子,从A(start)柱子,经由B(middle)柱子,到C(end)柱子。
步骤1.2:最大的盘子从A(start)柱子,一步,到B(end)柱子
步骤1.3:n-2个盘子,从C(start)柱子,经由A(middle)柱子,到B(end)柱子。

——————————————————————————————————————————————————

每次执行到递归语句时,就要开辟一个堆栈来存储hanoi(),包括盘子数n,柱子A,B,C的状态。回溯时会将之前的一次入栈数据弹出。

package Recursive;

import java.util.Scanner;

public class HanNuoTa {
	int n;
	char start;
	char middle;
	char end;
	int sum = 0;
//	将n个在start柱子上的盘子通过middle这个柱子移动到end这个柱子上
	public void hanoi(int n, char start, char middle, char end) {
		if (n == 1) {
			move(start, end);
			return;
		} else {
//			将A柱子上的n-1个盘子移动到end
			hanoi(n - 1, start, end, middle);
//			将A柱子上的最大的n盘移动到C柱子上
			move(start, end);
//			将在B柱子上的前n-1个盘子移动到C上
			hanoi(n - 1, middle, start, end);
		}
	}
	 
	public void move(char start, char end) {
		System.out.println("第"+(++sum)+"次:"+start + "->" + end);
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("盘子数:");
		int n = sc.nextInt();
		HanNuoTa h = new HanNuoTa();
		h.hanoi(n, 'A', 'B', 'C');
		sc.close();
	}
}


/**
 * 
 *         eg. 当有三个盘子时,在主函数中调用hanoi(3,A,B,C)时,
 *         进入函数中执行else后的一句,hanoi(2,A,C,B),
 *         并且在栈中为后续语句分配空间但不执行。接着再调用自身,hanoi(1,A,B,C),
 *         输出A->C;函数开始“归”,再执行调用hanoi(1,A,B,C)函数之后未执行的语句,
 *         “第二步”,由于这是在函数hanoi(2,A,C,B),故输出A->B;
 *         接着调用hanoi(1,C,A,B),输出C->B;接着“归”,这是将此时回到函数hanoi(3,A,B,C),
 *         输出A->C;接着调用函数hanoi(2,B,A,C) ,在调用hanoi(1,B,C,A)输出B->A;
 *         接着输出B->C;最后,调用hanoi(1,A,B,C)输出A->C.
 *
 */

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:终极编程指南 设计师:CSDN官方博客 返回首页
评论

打赏作者

东石有海

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值