簡明遞歸魔法

概述

漢諾塔是一個經典的遞歸問題,雖然說看人家寫好的算法程序就那麼幾行,但着實理解有必定的難度。查閱了一些資料,參閱別人的思路,對漢諾塔算法進行一番梳理。算法

問題來源

有一個梵塔,塔內有三個座A、B、C,A座上有若干個盤子,盤子大小不等,大的在下,小的在上(如圖)。spa

須要把這些個盤子從A座移到C座,中間能夠借用B座,但每次只容許移動一個盤子,而且在移動過程當中,3個座上的盤子始終保持大盤在下,小盤在上。code

實例

咱們從簡單的來看一下如何移動。遞歸

只有一個盤子程序

傻子也知道,直接移動到C座就OK了。方法

有兩個盤子 [1 2]im

  1. 把1移動到B總結

  2. 把2移動到Cstatic

  3. 把1移動到Cimg

有三個盤子 [1 2 3]

  1. 把1移動到C

  2. 把2移動到B

  3. 把1移動到B

  4. 把3移動到C

  5. 把1移動到A

  6. 把2移動到C

  7. 把1移動到C

雖然說好像感受有必定規律,但好像說不出來個因此然。

規律

對於上面的例子,咱們能夠總結出兩點:

  1. 最後一步確定是把1移動到C

  2. 若是盤子數大於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的角色)。

代碼實現(Java)

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 始終表明堆着盤子的座。

相關文章
相關標籤/搜索