您的位置:首页 > 理论基础 > 数据结构算法

【算法笔记】使用栈实现汉诺塔(Hanoi)经典算法

2017-09-28 23:44 585 查看
汉诺塔(Hanoi)算法,应该是每一个程序员都会学习到的递推算法之一,汉诺塔是一个很著名的智力题,但是这里就不科普它的由来了,我们直接进入正题。



如上图,假设A棒有五个原盘,依次移动,每次移动一块,小的永远只能在上面,最终移动到C棒上,如何用算法实现呢?

从这里移动的逻辑我们很容易发现,A帮不就像一个栈吗,栈顶必须先出,网上看过很多汉诺塔算法,很少涉及到用栈实现,的确,算法拿出来了,用什么都一样,在我学习的时候,教材上是用的char,直接模拟推算,没用真正移动数据实现真正的Hanoi思想,所以,琢磨了一会,写了一个用栈实现的算法。

首先,既然是栈,为了方便跟踪,写了一个自己的MyStack包装了一下Java的Stack,贴上代码:

class MyStack{
private String name;
private Stack<Integer> data;
public MyStack(String name){
this.name=name;
data=new Stack<>();
}
public String getName(){
return this.name;
}
public void push(int data){
if(!this.data.isEmpty()&&this.data.peek()<data){
System.out.println("出错");
}
this.data.push(data);
}
public int peek(){
return data.peek();
}
public int pop(){
return data.pop();
}
public int size(){
return data.size();
}
public boolean isEmpty(){
return data.isEmpty();
}
}


然后就是Hanoi递推的实现,还是贴上图片



我们要按照Hanoi的逻辑将原盘从A棒移动到C棒,那么,就必须以B棒作为媒介,

最大的必须在最下面,所以,我们必须把上面4个圆盘先移动到B棒上。

但是又看,上面4个,要想把第四个移动到B棒,就得用C棒暂时当媒介,先把上面3个移动到C棒,以此类推,知道只有最后一个的时候,就可以直接移动了,所以,我们要做的就是想出一个算法,递推到只剩一个圆盘,然后慢慢回栈,到第二,第三,第四,最后第五。

先贴上代码:

public static void hanoi(int size,MyStack a,MyStack b,MyStack c){
if(size==1){
c.push(a.pop());
}else{
int n=b.size();
hanoi(size-1,a,c,b);
c.push(a.pop());
hanoi(b.size()-n,b,a,c);
}
}


首先解释参数中的size,因为栈无法在不取出元素的情况下递减长度,所以增加了参数size作为栈的圆盘指针,限定只能移动size个圆盘。

所以,当size==1的时候,就是只剩下一个圆盘,那么就顺理成章的直接移动到C棒了,不必在意此时的C棒回栈后是B棒还是A棒,那不是此时递归该担心的事情。

在算法中,如果size!=1,那么说明我们需要一个作为媒介,让size-1个圆盘先暂时放到媒介上,然后将第size个圆盘放过去,所以,我们需要进行一次递归,将第size-1个上面的圆盘重新进行计算该存放的位置,计算完成后,然后放入第size个圆盘到C帮,然后再将媒介B棒中的圆盘又以A棒为媒介,以此方式放入C盘。

至于为什么要在递归前缓存一次B棒的size,因为进入递归前不知道B棒是否有数据,说不定此次计算正是上一次的递归呢,不知道后面的方法B棒会是怎样的存在,不知道会进入多少次递归,假设B棒在进入第一次递归前长度为2,递归完后,长度为5,第二次递归时,如果不限制size长度,直接使用B棒的size,那么,除了第一次递归时增加的3个数据,还会把原本的2个数据一起计算进去,博主就在这个坑绕了一些时间,最后跟踪了一下才明白这个。

文字有点多,最后贴上完整代码:

public class Hanoi {
private static int m=1;
private static MyStack a=new MyStack("A");
private static MyStack b=new MyStack("B");
private static MyStack c=new MyStack("C");
public static void main(String[] args) {
for(int i=5;i>0;i--){
a.push(i);
}
hanoi(a.size(),a,b,c);
print(c);
}
public static void hanoi(int size,MyStack a,MyStack b,MyStack c){
if(size==1){
System.out.println("第"+m+++"步,从"+a.getName()+"移动了 "+a.peek()+"到了"+c.getName());
c.push(a.pop());
}else{
int n=b.size();
hanoi(size-1,a,c,b);
System.out.println("第"+m+++"步,从"+a.getName()+"移动了 "+a.peek()+"到了"+c.getName());
c.push(a.pop());
hanoi(b.size()-n,b,a,c);
}
}

public static void print(MyStack temp){
System.out.println("size:"+temp.size());
for(int i=0,n=temp.size();i<n;i++){
System.out.print(temp.pop()+" ");
}
System.out.println();
}
}
class MyStack{ private String name; private Stack<Integer> data; public MyStack(String name){ this.name=name; data=new Stack<>(); } public String getName(){ return this.name; } public void push(int data){ if(!this.data.isEmpty()&&this.data.peek()<data){ System.out.println("出错"); } this.data.push(data); } public int peek(){ return data.peek(); } public int pop(){ return data.pop(); } public int size(){ return data.size(); } public boolean isEmpty(){ return data.isEmpty(); } }


核心算法只有那一段,其他是我为了方便跟踪增加的,各位在测试的时候可以选择性删除。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐