Java实现汉诺塔问题
1 问题描述
Simulate the movement of the Towers of Hanoi Puzzle; Bonus is possible for using animation.
e.g. if n = 2 ; A→B ; A→C ; B→C;
if n = 3; A→C ; A→B ; C→B ; A→C ; B→A ; B→C ; A→C;
翻译:模拟汉诺塔问题的移动规则;获得奖励的移动方法还是有可能的。
相关经典题目延伸:
引用自百度百科:
有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少需要多少次移动,设移动次数为H(n)。
首先我们肯定是把上面n-1个盘子移动到柱子B上,然后把最大的一块放在C上,最后把B上的所有盘子移动到C上,由此我们得出表达式:
H⑴ = 1 A—>C
H(2) = 3 A—>B;A—>C;B—>C
H(3) = 7 …
H(4) = 15
… …
H(n) = 2*H(n-1)+1 (n>1)
那么我们很快就能得到H(n)的一般式:
H(n) = 2^n - 1 (n>0)
2 解决方案
2.1 递归法
import java.util.Scanner;
public class Hanoi {
//使用递归法求解含有n个不同大小盘子的汉诺塔移动路径,参数n为盘子数,把A塔上盘子全部移动到C塔上,B为过渡塔
public static void recursionHanoi(int n,char A,char B,char C){
if(n == 1){
System.out.print(A+"——>"+C+"\n");
}
else{
recursionHanoi(n-1,A,C,B); //使用递归先把A塔最上面的n-1个盘子移动到B塔上,C为过渡塔
System.out.print(A+"——>"+C+"\n"); //把A塔中底下最大的圆盘,移动到C塔上
recursionHanoi(n-1,B,A,C); //使用递归把B塔上n-1个盘子移动到C塔上,A为过渡塔
}
}
public static void main(String[] args){
System.out.println("请输入盘子总数n:");
Scanner in = new Scanner(System.in);
int n = in.nextInt();
recursionHanoi(n,'A','B','C');
}
}
运行结果:
请输入盘子总数n:
A——>B
A——>C
B——>C
请输入盘子总数n:
A——>C
A——>B
C——>B
A——>C
B——>A
B——>C
A——>C
2.2 非递归法
要使用非递归方法,首先要解决的核心问题就是找到汉诺塔的移动规律。现在问题是把n个盘子从A塔全部移动到C塔,那么先看看n = 2,3,4时,其具体盘子的移动路径结果(PS:移动路径前标号是指A塔上原有的第几个盘子,如(1)代表A塔上原有最上面的那个盘子,依次类推…):
n = 2:
(1)A—>B
(2)A—>C
(1)B—>C
n = 3:
(1)A——>C
(2)A——>B
(1)C——>B
(3)A——>C
(1)B——>A
(2)B——>C
(1)A——>C
n = 4:
(1)A——>B
(2)A——>C
(1)B——>C
(3)A——>B
(1)C——>A
(2)C——>B
(1)A——>B
(4)A——>C
(1)B——>C
(2)B——>A
(1)C——>A
(3)B——>C
(1)A——>B
(2)A——>C
(1)B——>C
从上我们可以发现,n为偶数2,4时路径前标号为(1)的盘子移动路径依次为A——>B——>C,A——>B——>C——>A——>B——>C。n为偶数2,4时路径前标号为(2)的盘子移动路径依次为A—>C,A—>C——>B——>A—>C。而且发现n = 4其标号为(1)和标号为(3)的移动路径一模一样。n为奇数3时路径前标号为(1)和(2)的盘子移动路径依次为A——>C——>B——>A——>C,A——>B——>C。
看到这里,我们可以大胆猜测盘子的具体移动路径与盘子的总个数的奇偶性以及盘子标号的奇偶性有关,而且移动的路径是固定又循环的。
那么现在设定一个二维数组用来存放盘子下次移动的塔:
char next = new char[2][3];
二维数组中行char[0]代表数组下标为偶数的盘子下次要移动的塔
二维数组中行char[1]代表数组下标为奇数的盘子下次要移动的塔
二维数组重列char[0][0]代表盘子现在在A塔准备进行下次移动
二维数组重列char[0][1]代表盘子现在在B塔准备进行下次移动
二维数组重列char[0][2]代表盘子现在在C塔准备进行下次移动
那么下面我们就来根据盘子现在所在塔,设定其下次移动的目的塔(PS:设共有n的盘子):
if(n为偶数)
{
//数组下标为偶数的盘子移动目的塔,注意上面示例的标号为(1),其数组下标为0
next[0][0] = ‘B’; //看n = 4的移动路径中(1)A——>B
next[0][1] = ‘C’; //看n = 4的移动路径中(1)B——>C
next[0][2] = ‘A’; //看n = 4的移动路径中(1)C——>A
//数组下标为奇数的盘子移动目的塔
next[1][0] = ‘C’; //看n = 4的移动路径中(2)A——>C
next[1][1] = ‘A’; //看n = 4的移动路径中(2)B——>A
next[1][0] = ‘B’; //看n = 4的移动路径中(2)C——>B
}
If(n为奇数)
{
//数组下标为偶数的盘子移动目的塔,注意上面示例的标号为(1),其数组下标为0
Next[0][0] = ‘C’; //看n = 3的移动路径中(1)A——>C
Next[0][1] = ‘A’; //看n = 3的移动路径中(1)B——>A
Next[0][2] = ‘B’; //看n = 3的移动路径中(1)C——>B
//数组下标为奇数的盘子移动目的塔
Next[1][0] = ‘B’; //看n = 3的移动路径中(2)A——>B
Next[1][1] = ‘C’; //看n = 3的移动路径中(2)B——>C
Next[1][2] = ‘A’; //此处根据观察规律假设的
}
到这里,距离使用非递归法解决汉诺塔问题已经有头绪了,此处还有注意一点就是H(n) = 2^n - 1 (n>0),即移动n个盘子需要总次数为2^n - 1 ,即使用非递归法是需要进行循环2^n - 1 次。
package com.liuzhen.ex2;
import java.util.Scanner;
public class Hanoi {
//使用递归法求解含有n个不同大小盘子的汉诺塔移动路径,参数n为盘子数,把A塔上盘子全部移动到C塔上,B为过渡塔
public static void recursionHanoi(int n,char A,char B,char C){
if(n == 1){
System.out.print(A+"——>"+C+"\n");
}
else{
recursionHanoi(n-1,A,C,B); //使用递归先把A塔最上面的n-1个盘子移动到B塔上,C为过渡塔
System.out.print(A+"——>"+C+"\n"); //把A塔中底下最大的圆盘,移动到C塔上
recursionHanoi(n-1,B,A,C); //使用递归把B塔上n-1个盘子移动到C塔上,A为过渡塔
}
}
public static void noRecursionHanoi(int n){
if(n<=0){
throw new IllegalArgumentException("n must be >=1");
}
char[] hanoiPlate = new char[n]; //记录n个盘子所在的汉诺塔(hanoiPlate[1]='A'意味着第二个盘子现在在A上)
char[][] next = new char [2][3]; //盘子下次会移动到的盘子的可能性分类
int[] index = new int[n];
//根据奇偶性将盘子分为两类
for(int i=0;i<n;i=i+2){
index[i]=0;
}
for(int i=1;i<n;i=i+2){
index[i]=1;
}
//一开始所有盘子都在A上
for(int i=0;i<n;i++){
hanoiPlate[i]='A';
}
//n的奇偶性对移动方式的影响
if(n%2==0){
//数组下标为偶数的盘子移动目的塔,注意上面示例的标号为(1),其数组下标为0
next[0][0]='B';
next[0][1]='C';
next[0][2]='A';
//数组下标为奇数的盘子移动目的塔
next[1][0]='C';
next[1][1]='A';
next[1][2]='B';
}
else
{
//数组下标为偶数的盘子移动目的塔,注意上面示例的标号为(1),其数组下标为0
next[0][0]='C';
next[0][1]='A';
next[0][2]='B';
//数组下标为奇数的盘子移动目的塔
next[1][0]='B';
next[1][1]='C';
next[1][2]='A';
}
//开始移动
for(int i=1;i<(1<<n);i++){ //总共要执行2^n-1(1<<n-1)步移动
int m=0; //m代表第m块盘子hanoiPlate[m]
//根据步骤数i来判断移动哪块盘子以及如何移动
for(int j=i;j>0;j=j/2){
if(j%2!=0){ //此步骤光看代码代码有点抽象,建议手动写一下n = 2时的具体移动路径的j、m值变化
System.out.println("("+(m+1)+")"+hanoiPlate[m]+"->"+next[index[m]][hanoiPlate[m]-'A']);
hanoiPlate[m]=next[index[m]][hanoiPlate[m]-'A'];
break; //移动盘子后则退出这层循环
}
m++;
}
}
}
public static void main(String[] args){
System.out.println("请输入盘子总数n:");
Scanner in = new Scanner(System.in);
int n = in.nextInt();
recursionHanoi(n,'A','B','C');
System.out.println("非递归法结果:");
noRecursionHanoi(n);
System.out.println();
}
}
运行结果:
请输入盘子总数n:
A——>B
A——>C
B——>C
非递归法结果:
(1)A->B
(2)A->C
(1)B->C
请输入盘子总数n:
A——>C
A——>B
C——>B
A——>C
B——>A
B——>C
A——>C
非递归法结果:
(1)A->C
(2)A->B
(1)C->B
(3)A->C
(1)B->A
(2)B->C
(1)A->C
Java实现汉诺塔问题的更多相关文章
- Java-Runoob-高级教程-实例-方法:03. Java 实例 – 汉诺塔算法-un
ylbtech-Java-Runoob-高级教程-实例-方法:03. Java 实例 – 汉诺塔算法 1.返回顶部 1. Java 实例 - 汉诺塔算法 Java 实例 汉诺塔(又称河内塔)问题是源 ...
- java 解决汉诺塔问题
//汉诺塔问题//HanYang 2016/10/15 import java.util.Scanner; //输出public class Hanuota { public static void ...
- java实现汉诺塔算法
package com.ywx.count; import java.util.Scanner; /** * @author Vashon * date:20150410 * * 题目:汉诺塔算法(本 ...
- java实现汉诺塔计数
** 汉诺塔计数** 汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具. 大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下 ...
- Java递归算法——汉诺塔问题
//================================================= // File Name : Tower_demo //-------------------- ...
- Java求解汉诺塔问题
汉诺塔问题的描述如下:有3根柱子A.B和C,在A上从上往下按照从小到大的顺序放着一些圆盘,以B为中介,把盘子全部移动到C上.移动过程中,要求任意盘子的下面要么没有盘子,要么只能有比它大的盘子.编程实现 ...
- Java实现汉诺塔移动,只需传一个int值(汉诺塔的阶)
public class HNT { public static void main(String[] args) { HNT a1 = new HNT(); a1.lToR(10); //给汉诺塔a ...
- 算法笔记_013:汉诺塔问题(Java递归法和非递归法)
目录 1 问题描述 2 解决方案 2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus ...
- 编程:递归编程解决汉诺塔问题(用java实现)
Answer: //Li Cuiyun,October 14,2016. //用递归方法编程解决汉诺塔问题 package tutorial_3_5; import java.util.*; publ ...
随机推荐
- 整理了最全的Python3数据类型转换方法,可以收藏当手册用
本文基于python3.8版本,总结了各种数据类型直接的转换规则和方法.算是比较全了,可以收藏当手册来查. 概述 数据类型转换,指的是通过某种方法,将一个数据由原来的类型转换为另外一个类型.比如,我们 ...
- HDU 2000 (水)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2000 题目大意:仨字符从小到大排序 解题思路: 很水很水,需要注意的地方是如果用苦力(三个if)要注意 ...
- python实现边缘提取
1. 题目描述 安装opencv环境,实现边缘提取 2. 实现过程 1. 安装opencv+python环境 2. 打开图片 3. 将图片二值化 4. 提取边缘 5. 显示图片 3. 运行结果 ...
- python--字典基本操作
字典 格式 key :value # string list dict# 1.取数据方便# 2.速度快, 定义一个空字典: d = dict() 或者 d = { } infos = {'n ...
- 到手的DEM不会用?教你6个常用强大功能
一.概述 DEM是数字高程模型(Digital Elevation Model)的简称,接触GIS,规划,设计类的多多少少会接触到DEM,可是这个直接查看黑溜溜一片DEM到底可以用来做什么呢? 二.背 ...
- 力扣题解-LCP 06. 拿硬币
题目描述 桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中.我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数. 示例 1: 输入:[4,2,1] 输出:4 解释: ...
- eslint插件开发教程
开发eslint插件目的:根据项目需要,自定义满足项目特殊需要的校验规则 参考eslint官方文档展开阐述 插件开发 自定义规则 单元测试 下面开始通过一个示例demo来介绍插件整个开发流程 代码中出 ...
- ESlint中console.log报错问题
ESlint中console.log报错问题 由于ESlint规范化,导致console.log的使用也会报错,下面是设置允许console.log控制台输出 描述:打开 package.json 文 ...
- SQL——SELECT、UPDATE、DELETE和INSERT INTO
SQL是一种ANSI的标准计算机语言.ANSI:美国国家标准化组织.除SQL标准外,大部分SQL数据库都拥有私有的扩展.SQL对大小写不敏感.某些数据库系统要求在SQL命令末端使用分号,这样可以执行一 ...
- Java——反射三种方式的效率对比
转载自:https://blog.csdn.net/aitcax/article/details/52694423 1 使用field(效率最高) long start = S ...