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

来源:互联网 发布:四方麻将 河南软件 编辑:程序博客网 时间:2024/06/10 18:33

汉诺塔(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();    }}

核心算法只有那一段,其他是我为了方便跟踪增加的,各位在测试的时候可以选择性删除。

原创粉丝点击