漢諾塔問題算法

問題

漢諾塔(又稱河內塔)問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞着64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。

遞歸算法

在這裏插入圖片描述
我們這裏先列舉盤子的個數爲1,2,3,4的時候的情況,具體的如下表格。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
觀察上面的四個表格數據,我們可以得到以下結論:
1.移動的步數必定爲奇數步
2.(1)中間一步是把最大的一個盤子由A移動到C上去
(2)中間一步之上可以看成把A上n-1個盤子通過藉助輔助塔(C塔)移到了B上
(3)中間一步之下可以看成把B上n-1個盤子通過藉助輔助塔(A塔)移到了C上

非遞歸算法

一個美國學者總結得到:所有的漢諾塔移動可以總結爲重複的兩步。
我們假設現在最小的圓盤在A柱子上,柱子爲A,B,C
第一步:將最小圓盤移動到下一個柱子上,也就是B
第二步:對A柱子和C柱子進行頂上最小的元素進行判斷,把小一點的那個圓盤移動到大一點的那個圓盤(有空則摞在空柱子上)。
重複上述兩步就可以得到答案。注意:這樣得到的最後的答案不一定是摞在C上,如果N是奇數將摞在B上,所以如果N是奇數我們就令第二個柱子爲C,第三個柱子爲B,這樣就一定最後是摞在C上的。

爲了形象地說明這個過程,下面以n=3時爲例
在這裏插入圖片描述

實現代碼

解決漢諾塔問題的算法代碼如下:

package com.general.arithmetic.practice;

import java.util.ArrayList;
import java.util.Stack;

/***
 * 漢諾塔問題的學習與練習
 * 問題描述:從左到右有A、B、C三根柱子,其中A柱子上面有從小疊到大的n個圓盤,現要求將A柱子上的圓盤移到C柱子上去,期間只有
 * 一個原則:一次只能移動一個盤子且大盤子不能在小盤子上面,求移動的步驟和移動的次數。
 *
 * 漢諾塔的非遞歸算法:
 * 一個美國學者總結得到:所有的漢諾塔移動可以總結爲重複的兩步,我們假設現在最小的圓盤在A柱子上,柱子A、B、C
 * 第一步:將最小圓盤移動到下一個柱子上,也就是B
 * 第二步:對A柱子和C柱子進行頂上最小的元素進行判斷,把小一點的那個圓盤移動到大一點的那個圓盤(有空則放置在空柱子上)
 * 重複上述兩步就可以得到答案。
 *
 * **/
public class Hanoi {
    public static int count=0;
    public static Stack<Integer> plate=new Stack<Integer>();
    public static int PLATE_COUNT=3;

    public static ArrayList<Stack<Integer>> plate_array=new ArrayList<>();
    public static char s[]={'A','B','C'};

    public static void move_recursion(int n,char A,char C){

        count++;
        System.out.println("plate "+plate.search(n)+" move:"+A+"-------->"+C);
    }
    public static void hanoi_tower(int n,char A,char B,char C){

        if(n==1){
            move_recursion(1,A,C);
        }else{
            hanoi_tower(n-1,A,C,B);
            move_recursion(n,A,C);
            hanoi_tower(n-1,B,A,C);
        }
    }
    public static boolean move(int before,int after){
        if(plate_array.get(before).empty()){
            return false;
        }
        if (!plate_array.get(after).empty()){
            if((plate_array.get(after).peek()-plate_array.get(before).peek())<0){
                return false;
            }
        }
        plate_array.get(after).push(plate_array.get(before).peek());
      ;
        System.out.println("plate "+ plate_array.get(before).pop()+" move:"+s[before]+"-------->"+s[after]);
        return true;
    }

    public static void hanoi_tower(){
        int count=0;
        if(PLATE_COUNT%2==1){
            s[1]='C';
            s[2]='B';
        }
        while (true){
            ++count;
            move((count - 1) % 3, (count) % 3 );
            if (!move((count - 1) % 3 , (count + 1) % 3 )&&!move((count + 1) % 3, (count - 1) % 3 ))
                break;


        }
    }

    public static void main(String[] args){
        if(plate.size()>0){
            plate.clear();
        }
        for(int i=PLATE_COUNT;i>0;i--){
            plate.push(i);
        }
        System.out.println("遞歸求移動漢諾塔的步驟:");
        hanoi_tower(PLATE_COUNT,'A','B','C');
        System.out.println("總共移動"+count+"次");
        System.out.println("*********************");
        System.out.println("非遞歸求移動漢諾塔的步驟");
        for(int i=0;i<3;i++){
            plate_array.add(new Stack<Integer>());
        }
        for(int i=PLATE_COUNT;i>0;i--){
            plate_array.get(0).push(i);
        }
        hanoi_tower();

    }

}

代碼運行結果如下:

遞歸求移動漢諾塔的步驟:
plate 1 move:A-------->C
plate 2 move:A-------->B
plate 1 move:C-------->B
plate 3 move:A-------->C
plate 1 move:B-------->A
plate 2 move:B-------->C
plate 1 move:A-------->C
總共移動7次
*********************
非遞歸求移動漢諾塔的步驟
plate 1 move:A-------->C
plate 2 move:A-------->B
plate 1 move:C-------->B
plate 3 move:A-------->C
plate 1 move:B-------->A
plate 2 move:B-------->C
plate 1 move:A-------->C

參考鏈接:

https://blog.csdn.net/qq_19446965/article/details/81591945
https://blog.csdn.net/yhf_naive/article/details/53384148

轉載請註明出處,謝謝合作!如果喜歡此文章不妨點個贊,也可以來下關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章