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 ...
随机推荐
- webpack3 项目升级 webpack4
由于 vue-cli 2 构建的项目是基于 webpack3,所以只能自己动手改动,至于升级 webpack4之后提升的编译速度以及各种插件自己去体验. 修改配置 1.替换插件 extract-tex ...
- python 基础应用5-简单购物车
1.列表去重 #列表去重 li = [1,2,33,33,2,1,4,5,6,6] set1 = set(li)# 转为集合 li = list(set1)# 转为列表 print(li)#[1, 2 ...
- 关于redis内存分析,内存优化
对于redis来说,什么是最重要的? 毋庸置疑,是内存. 一.reids 内存分析 redis内存使用情况:info memory 示例: 可以看到,当前节点内存碎片率为226893824/20952 ...
- 对background: url("~assets/img/common/collect.svg") 0 0/14px 14px 的理解
需求:给收藏数字前面通过::before伪元素添加图标 相关代码: .goods-info .collect { position: relative; } .goods-info .collect: ...
- Storm-Mongodb详解
这两天研究Trident,踩坑踩的遭不住,来和大家分享下. 首先写入数据库: 先看官方API给我们提供的方法: //这是用来辅助下面MongoInsert的简单实现类 public class Sim ...
- http://blog.itpub.net/28602568/viewspace-759789/
varchar .varchar2.nvarchar.nvarchar2 -->存储可变的字符串 varchar .varchar2:varchar:汉字全角等字符占2字节,数字.字母均1个字 ...
- 蓝桥杯 试题 历届试题 发现环 并查集+dfs
问题描述 小明的实验室有N台电脑,编号1~N.原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络.在树形网络上,任意两台电脑之间有唯一的路径相连. 不过在最近一次维护网络时,管理员误操作使 ...
- .Net基础之2——C#基础
1.注释符的作用 1).注销 2).解释 2.C#中的3种解释符 1).单行注释(//要注释的内容) //这行代码的作用是将hello world输出到控制台上 ...
- Excel_通过单元格的值来引用以单元格值命名的sheet
1.通过单元格的值来引用以单元格值命名的sheet,在做多个类似sheet的统计结果时效率比较高 当一项测试中有很多个模块,每个模块中有很多条测试用例 将以上测试用例整理在Excel中,每个模块一个s ...
- ajax 请求PHP返回json格式的处理
php返回代码格式 public function json(){ if (request()->isAjax()){ $data = [ 'code'=>'1', 'msg'=>' ...