本文目录
- 汉诺塔问题介绍
- 建模说明
- 解决思路说明
- 具体代码
一、汉诺塔问题介绍
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
二、建模说明
创建3个ArrayList类型的静态变量,分别命名为A、B、C,代表三根柱,而数组列表内则通过填充整型数据代表盘子,数字代表盘子的半径大小,如
----------------------------------
A:n (n-1) (n-2) .... 1
B:
C:
----------------------------------
即代表汉诺塌盘子为6时的初始状态,6个盘子依次(数组列表索引从小到大代表柱子从下到上)排列在A柱上。
通过把数组列表的最后一个元素移除和增加来模拟盘子的移动,这样子的建模打印出来以后可以很直观的看清楚汉诺盘子的位置与移动轨迹。
三、解决思路说明
1.创建如下方法 private static void moveDisks(int n, ArrayList<Integer> fromTower, ArrayList<Integer> toTower, ArrayList<Integer> auxTower)
n代表需要移动的盘子数目,fromTower代表盘子目前所在柱子,toTower代表盘子需要去到的柱子,auxTower代表最后一个用于辅助中转过渡的柱子。通过这个方法的递归解决汉诺塔
2.在这个方法里面有两种状态,状态1是基础状态,即只有一个盘子需要移动,则很简单的把盘子从fromTower柱移动到toTower柱(调用方法public static void move(ArrayList<Integer> fromTower, ArrayList<Integer> toTower)解决)。状态2是常规状态,即多于1个盘子需要移动,而这n(n>1)个盘子的移动最后都要分解为状态1去解决。
3.现在说明状态2,即需要移动的盘子的个数n大于1的情况。
----------------------------------
A:n (n-1) (n-2) .... 1
B:
C:
----------------------------------
如图,现在为初始状态,把A柱看成两部分,最底下的半径为n的盘子,上面的(n-1)个盘子,那么要移动到B柱的的步骤如下:
1.把上面的(n-1)个柱子移动到C柱,即调用方法 moveDisks((n-1),A,C,B)
----------------------------------
A:n
B:
C: (n-1) (n-2) .... 1
----------------------------------
2.把半径为n的盘子移动到B柱,即状态1,调用方法move(A,B)
----------------------------------
A:
B: n
C: (n-1) (n-2) .... 1
----------------------------------
3.把C柱的(n-1)个盘子移动到B柱,即调用方法moveDisks((n-1),C,B,A)
----------------------------------
A:
B: n (n-1) (n-2) .... 1
C:
----------------------------------
在上面的步骤1和步骤3中又是同样的分析过程,比如步骤3可以看作现在有(n-1)个盘子要从C柱移动到B柱,而B柱原本有的半径为n的盘子因为是最大半径的盘子,所以不会对移动产生任何影响,因此可以直接忽略它,所以这个步骤就简化为了C柱有(n-1)个盘子要移动到B柱去。通过这样子不断的简化,递归调用 moveDisks()方法就可以解决问题。
3.如果还是没想明白,可以直接通过运行程序,看输出文件,就可以很直观的看懂整个移盘子的步骤。如下图为盘子为4时的输出文件效果
四、具体代码
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;
public class TowerOfHanoi {
//三个数组列表分别表示三根柱子,count表示调换次数,numberOfDisks表示盘子总数
private ArrayList<Integer> A = new ArrayList<>();
private ArrayList<Integer> B = new ArrayList<>();
private ArrayList<Integer> C = new ArrayList<>();
private PrintWriter output;
private int count = 0;
private int numberOfDisks =0;
public static void main(String[] args) throws FileNotFoundException{
for(int i=1;i<=5;i++) {
//调用方法解决问题
new TowerOfHanoi(i).moveDisks();
}
}
public TowerOfHanoi(int n) throws FileNotFoundException {
this.numberOfDisks = n;
//在当前目录新建TowerOfHanoi_n.txt用于存储调换过程
this.output = new PrintWriter(new File("TowerOfHanoi_"+n+".txt"));
//初始化柱子
for(int i=n;i>0;i--) {
this.A.add(i);
}
//把初始状态存入文件
this.output.println("总盘子数目:"+n);
this.output.println("---------初始状态---------");
this.output.print("A: ");
for(Integer i:this.A) this.output.print(i+" ");
this.output.println();
this.output.print("B: ");
for(Integer i:this.B) this.output.print(i+" ");
this.output.println();
this.output.print("C: ");
for(Integer i:this.C) this.output.print(i+" ");
this.output.println();
}
public void moveDisks() {
this.moveDisks(this.numberOfDisks,this.A,this.B,this.C);
this.output.close();
System.out.println("已完成调换");
}
public void moveDisks(int n, ArrayList<Integer> fromTower, ArrayList<Integer> toTower, ArrayList<Integer> auxTower) {
if(n == 1) {
move(fromTower,toTower); //当需要移动的盘子只有1个时,可以直接进行移动,而不需要递归
//每一次调换均输出当前A、B、C柱的情况到文件中做记录
this.output.println("--------第"+(++this.count)+"次调换--------");
this.output.print("A: ");
for(Integer i:this.A) this.output.print(i+" ");
this.output.println();
this.output.print("B: ");
for(Integer i:this.B) this.output.print(i+" ");
this.output.println();
this.output.print("C: ");
for(Integer i:this.C) this.output.print(i+" ");
this.output.println();
System.out.println("已完成第"+this.count+"次调换");
}
else {
//把上面(n-1)个盘子当做一个整体,从fromTower移动到auxTower
moveDisks(n-1,fromTower,auxTower,toTower);
//把fromTower仅剩下的最大半径的盘子移动到toTower(即只有一个盘子需要移动的情况)
moveDisks(1,fromTower,toTower,auxTower);
//把刚刚移动到C柱的(n-1)个盘子作为整体从auxTower移动到toTower
moveDisks(n-1,auxTower,toTower,fromTower);
}
}
public static void move(ArrayList<Integer> fromTower, ArrayList<Integer> toTower) {
//fromTower最大索引元素(即柱子最上面的盘子)的值添加到toTower,然后把这个元素从fromTower删除,
//通过这两步模拟盘子的移动
toTower.add(fromTower.get(fromTower.size()-1));
fromTower.remove(fromTower.size()-1);
}
}
Github地址:https://github.com/better-king/LearnJava/tree/master/src