漢諾塔是一個經典的遞歸問題,雖然說看人家寫好的算法程序就那麼幾行,但着實理解有必定的難度。查閱了一些資料,參閱別人的思路,對漢諾塔算法進行一番梳理。算法
有一個梵塔,塔內有三個座A、B、C,A座上有若干個盤子,盤子大小不等,大的在下,小的在上(如圖)。spa
須要把這些個盤子從A座移到C座,中間能夠借用B座,但每次只容許移動一個盤子,而且在移動過程當中,3個座上的盤子始終保持大盤在下,小盤在上。code
咱們從簡單的來看一下如何移動。遞歸
只有一個盤子程序
傻子也知道,直接移動到C座就OK了。方法
有兩個盤子
[1 2]
im
把1移動到B總結
把2移動到Cstatic
把1移動到Cimg
有三個盤子
[1 2 3]
把1移動到C
把2移動到B
把1移動到B
把3移動到C
把1移動到A
把2移動到C
把1移動到C
雖然說好像感受有必定規律,但好像說不出來個因此然。
對於上面的例子,咱們能夠總結出兩點:
最後一步確定是把1移動到C
若是盤子數大於1(1就直接移了),確定要把最大的盤子上面的所有盤子放到B上,而後將最大的放到C上
由上面的兩點,咱們來探究一下遞歸關係。
假設有n個盤子,分別標記爲 [1 2 3... n]
大數表示大盤子,已經在A座上放好。
初始狀態
A:有n個盤子 B:空 C:空
咱們如今有一個方法:hanoi(int n, String a, String b, String c)
,做用就是將n個盤子從a座藉助b座移動到c座。
咱們先不考慮它是怎麼移過去的。
下面咱們使用這個方法,結合上面咱們總結的規律進行移動盤子。
第一步,將
[1 2 3... n-1]
個盤子從A移動到B上,再將 n 盤子移動到C上
hanoi(n-1, A, C, B) // 將A上的n-1個盤子移動到B上 hanoi(1, A, B, C) // 將A上的一個盤子移動到C
如今咱們的盤子狀況爲
A:空 B:有n-1個盤子 C:最大的n盤子
因爲最大的n盤子上能夠聽任何盤子,你能夠徹底忽略它的存在,不用管它,這時候的情形(忽略n盤子的存在)是否是跟初始狀態相似呢(一個有盤子,其他的沒有盤子)?是的,只不過順序發生的變化而且盤子的數量減小了一個。
咱們的總盤子數爲:n-1
咱們的B座就是初始狀態的A座,A座就是初始狀態的B座,C座仍是C座。
第二步,將B座上的
[1 2 3... n-1-1]
從B移動到A上,將n-1盤子從B移動到C
hanoi(n-2, B, C, A) // 將B上的n-1-1個盤子移動到A上 hanoi(1, B, A, C) // 將B上的一個盤子移動到C
如今咱們的盤子狀況爲
A:有n-1-1個盤子 B:空 C:最大的n盤子和倒數第二個n-1盤子
C上的盤子已經擺好,能夠認爲是空座,是否是又回到了初始狀態的情形呢?是的,盤子數減一。
第三步,將A座上的
[1 2 3... n-2-1]
從A移動到B上,將n-2盤子從A移動到C
hanoi(n-3, A, C, B) // 將A上的n-2-1個盤子移動到A上 hanoi(1, A, B, C) // 將A上的一個盤子移動到C
又回到相似第一步執行後的情形了,如此反覆,直到全部盤子都成功移動到C上爲止。
通過上述的推敲,咱們知道,每通過一步,盤子數少一個,而且A和B兩座的位置互換(這裏指他們輪流充當初始狀態A的角色)。
public static void hanoi(int n, String a, String b, String c) { if (n == 1) { System.out.println("將" + a + "最上面的盤子移動到" + c); return; } // 當前盤子在a上,將當前盤子數-1放到b上 hanoi(n-1, a, c, b); // 剩下一個放到c上 hanoi(1, a, b, c); // 當前盤子在b上,b是下一輪的a, a b 換位置,進行下一輪 hanoi(n-1, b, a, c); }
調用:
hanoi(1, "A", "B", "C"); // 將A最上面的盤子移動到C hanoi(2, "A", "B", "C"); // 將A最上面的盤子移動到B // 將A最上面的盤子移動到C // 將B最上面的盤子移動到C hanoi(3, "A", "B", "C"); // 將A最上面的盤子移動到C // 將A最上面的盤子移動到B // 將C最上面的盤子移動到B // 將A最上面的盤子移動到C // 將B最上面的盤子移動到A // 將B最上面的盤子移動到C // 將A最上面的盤子移動到C
解釋一下a b c
和A B C
的關係
A B C
是實際的盤子座,a b c
表示的是一種狀態,即初始狀態a
表示有待移動的若干個盤子的座,b c
始終表示空座(c
上可能有盤子,但已經擺好,能夠認爲爲空)。
每移動一輪,A座與B座交換存盤子,若A有盤子,A就是參數a
,若B有盤子,B就是參數a
,C位置不動。
總之a
始終表明堆着盤子的座。