汉诺塔Java递归和非递归算法解析
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置n个金盘(如图1)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
这是一个典型的递归调用算法,要实现把n个圆盘从A移到C,我们只需要做到如下三步:
①:把n-1个圆盘从A移到B
②:把A最底层的圆盘从A移到C
③:把B上的n-1个圆盘移到C。
public class Hanio递归 {
static int i = 1;
public static void main(String[] args) {
Hanoi(3,"A","B","C");
}
public static void Hanoi(int num,String a,String b,String c){
if (num ==1 ){
System.out.println ("第 "+i+" 次移动,把编号为 1 的圆盘从 "+a+" 移动到 "+c);
i++;
}
else {
Hanoi(num - 1, a, c, b);
// 将起始柱上剩余的最后一个大圆盘移动到目标柱上
System.out.println("第 "+i+" 次移动,把编号为 "+num+" 的圆盘从 "+a+" 移动到 "+c);
i++;
// 递归调用 hanoi() 函数,将辅助柱上的 num-1 圆盘移动到目标柱上
Hanoi(num - 1, b, a, c);
}
}
}
Hanoi他问题的递归算法时间复杂度是O(2^n),而且递归算法调用空间是很大的,代码难写但简洁,计算机非常难受。
那么非递归算法该怎么实现呢
最简单的就是用栈模拟递归
import java.util.Stack;
public class Hanoi非递归 {
public static void main(String[] args) {
Hanoi("A","B","C",3);
}
public static void Hanoi(String a,String b,String c,int num){
Stack<State> starks = new Stack<> ();
State state = new State(a,b,c,num,false);
starks.push (state);
while (starks.size ()>0){
if(starks.peek ().title){
HanoiPrint(starks.pop ());
}
else {
State NumLinShi=starks.pop ();
if (NumLinShi.Num==1){
HanoiPrint(NumLinShi);
}
else {
State NumLinA_B = new State (NumLinShi.A,NumLinShi.C,NumLinShi.B,NumLinShi.Num-1,false);
State NumLinA_C = new State (NumLinShi.A,NumLinShi.B,NumLinShi.C,NumLinShi.Num,true);
State NumLinB_C = new State (NumLinShi.B,NumLinShi.A,NumLinShi.C,NumLinShi.Num-1,false);
starks.push (NumLinB_C);
starks.push (NumLinA_C);
starks.push (NumLinA_B);
}
}
}
}
public static void HanoiPrint(State str){
System.out.println ("把编号为\t"+str.Num+"\t的圆盘从\t"+str.A+"\t移动到\t"+str.C+"\t盘子上");
}
}
class State {
public String A;
public String B;
public String C;
public int Num;
public boolean title;
public State(String A, String B, String C , int Num,boolean title)
{
this.A = A;
this.B = B;
this.C = C;
this.Num = Num;
this.title = title;
}
}
先定义一个类名叫State,State有属性A,B,C,Num,title,A就是盘子的移动杆,B就是中间杆,C就是盘子的接受杆,Num就是被移动的盘子编号,title是一个标记,当title=true时表示该对象可以输出了。
这个代码的核心是Hanoi非递归类中的Hanoi方法,我们来拆解这个方法。
首先定义一个栈,把原始数据存入
Stack<State> starks = new Stack<> ();
State state = new State(a,b,c,num,false);
之后如果栈顶元素的Num=1,那就是只有一个盘子了就直接输出,
否则把栈顶元素按照如下规则一化三,栈顶元素出栈,以下三个元素进栈
①:把n-1个圆盘从A移到B
②:把A最底层的圆盘从A移到C
③:把B上的n-1个圆盘移到C。
while (starks.size ()>0){
if(starks.peek ().title){
HanoiPrint(starks.pop ());
}
else {
State NumLinShi=starks.pop ();
if (NumLinShi.Num==1){
HanoiPrint(NumLinShi);
}
else {
State NumLinA_B = new State (NumLinShi.A,NumLinShi.C,NumLinShi.B,NumLinShi.Num-1,false);
State NumLinA_C = new State (NumLinShi.A,NumLinShi.B,NumLinShi.C,NumLinShi.Num,true);
State NumLinB_C = new State (NumLinShi.B,NumLinShi.A,NumLinShi.C,NumLinShi.Num-1,false);
starks.push (NumLinB_C);
starks.push (NumLinA_C);
starks.push (NumLinA_B);
}
}
}
starks.push (NumLinB_C);
starks.push (NumLinA_C);
starks.push (NumLinA_B);
}
}
}