事先声明:博主在一本算法书上看到这个问题,对此有一些想法,有一部分出自抄腾,博主一心想表达自己对于处理问题的观点.对于此无需注明转发出处.此汉诺塔问题递归算法并未解决柱子还原之前不能为空问题,此种方法还有待优化.
汉诺塔问题一直是数据算法结构中比较经典的一个问题,但是还需要略微解释一下:相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。在这里我们来修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧的塔直接移动到最左侧,而必须经过中间。但当求N层的时候,打印最优的移动过程和最优的移动总步数。
例如:当塔层数为两层时,最上层的塔标记为1,最下层的塔标记为2,这打印:
Move 1 from left to mid
Move 1 from mid to right
Move 2 from left to mid
Move 1 from right to mid
Move 1 from mid to left
Move 2 from mid to right
Move 1 from left to mid
Move 1 from mid to right
It will move 8 steps。
要求:可以使用递归和非递归两种方法做,非递归算法用栈来模拟汉诺塔的三个塔。
递归的方法:首先,如果只剩最上层的塔需要移动,这需要有如下处理结果(左中右的移动方式)可以作为递归的终止条件,也就是只剩下上层塔的时候需要打印的结果。
1、如果希望从“左”移动到“中”,打印“Move 1 from left to mid”;
2、如果希望从“中”移动到“左”,打印“Move 1 from mid to left”;
3、如果希望从“中”移动到“右”,打印“Move 1 from mid to right”;
4、如果希望从“右”移动到“中”,打印“Move 1 from right to mid”;
5、如果希望从“左”移动到“右”,打印“Move 1 from left to mid”和“Move 1 from mid to right”;
6、如果希望从“右”移动到“左”,打印“Move 1 from right to mid”和“Move 1 from mid to left”;
如果剩下N层塔了,从上到下依次是1~N,这有以下判断。
1、如果剩下的N层塔都在“左”,希望全部移动到“中”,这需要三个步骤:
a.将1~N-1层塔全部从“左”移动到“右”,则交给递归过程;
b.将第N层塔从“左”移动到“中”;
c.再将1~N-1层塔全部从“右”移动到“中”,这个也交给递归过程
2、如果把剩下的N层从“中”移动到“左”,从“中”移动到“右”,从“右”移动到“中”,过程与情况1大致相同。
3、如果剩下的N层塔都在“左”,希望全部都要移动到“右”边去。则有五个步骤:
a.将1~N-1层塔先全部从“左”移动到“右”,由递归来完成.
b.将第N层塔从“左”移动到“中”.
c.将1~N-1层塔全部从“右”移动到“左”,由递归来完成.
d.将第N层塔从“中”移动到“右”.
e.最后将1~N-1层塔全部从“左”移动“右”,由递归来完成.
4、如果剩下的N层塔都在“右”塔,希望全部移动到“左”,过程与3基本一致,一样是分为五个步骤:
a.将1~N-1层塔先全部从“右”移动到“左”,由递归来完成.
b.将第N层塔从“右”移动到“中”.
c.将1~N-1层塔全部从“左”移动到“右”,由递归来完成.
d.将第N层塔从“中”移动到“左”.
e.最后将1~N-1层塔全部从“右”移动“左”,由递归来完成.
以上则为规律性详细解释。
代码实现:
package com.hekaikai666.test3;
/**
* 汉诺塔问题
* @author hekaikai666
* @time 2018年8月27日下午3:37:14
**/
public class hanoiProblem {
// 定义方法出啊如左中右的值
public int hanoiProblem1(int num, String left, String mid, String right) {
if (num < 1) {
return 0;
}
return process(num, left, mid, right, left, right);
}
// 显示传递的方法,逻辑
public int process(int num, String left, String mid, String right, String from, String to) {
if (num == 1) {
if (from.equals(mid) || to.equals(mid)) {
System.out.println("Move 1 from " + from + " to " + to + ";");
return 1;
} else {
System.out.println("Move 1 from " + from + " to " + mid + ";");
System.out.println("Move 1 from " + mid + " to " + to + ";");
return 2;
}
}
if (from.equals(mid) || to.equals(mid)) {
String another = (from.equals(left) || to.equals(left)) ? right : left;
int part1 = process(num - 1, left, mid, right, from, another);
int part2 = 1;
System.out.println("Move " + num + " from " + from + " to " + to + ";");
int part3 = process(num - 1, left, mid, right, another, to);
return part1 + part2 + part3;
} else {
int part1 = process(num - 1, left, mid, right, from, to);
int part2 = 1;
System.out.println("Move " + num + " from " + mid + " to " + to + ";");
int part3 = process(num - 1, left, mid, right, to, from);
int part4 = 1;
System.out.println("Move " + num + " from " + mid + " to " + to + ";");
int part5 = process(num - 1, left, mid, right, from, to);
return part1 + part2 + part3 + part4 + part5;
}
}
}
仅仅使用递归算法就是不断的判断移动的位置和两边位置的不同当然 这样处理会很麻烦,因为不断的调用当前的值,然后去判断下一个,这样难度就好像几何倍数的增加,但是我们有没有一种方法能够使用性处理运算呢,就比如说,我们挪动的时候只需要考虑存放的位置而不去考虑左右的大小,那么我们可以选择使用栈的非递归方式来处理,反而更加简便.