相信學過《資料結構與演算法》這門課程的同學都有聽過漢諾塔問題,但是可能在大學的時候沒有鑽研過,或者在學的時候就沒有弄懂,導致沒有很好的理解漢諾塔的經典解法,下面讓我來給大家來分析一下。
漢諾塔(又稱河內塔)問題是源於印度乙個古老傳說的益智玩具。大梵天創造世界的時候做了三個金剛石塔,在乙個塔上從下往上按照大小順序摞著64片**圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在最後乙個塔上。並且規定,在小圓盤上不能放大圓盤,在三個塔之間一次只能移動乙個圓盤。
對圓盤編號1~n,數字大的圓盤比數字小的圓盤體積大。並且引入起始塔,中轉塔,目標塔的概念,在演算法執行的時候,這3個塔的身份可能會互相變換。
1.如果現在在塔a上面只有1個圓盤,那麼直接把圓盤移動到塔c即可;
2.如果現在在塔a上面有2個圓盤,那麼先把圓盤1從塔a移動到塔b,再把圓盤2從塔a移動到塔c,最後把圓盤1從塔b移動到塔c;
3………
4.如果塔a有n個圓盤,那麼需要先把圓盤n之前的(圓盤n-1~ 圓盤1)從塔a先移動到塔b,再把圓盤n從塔a移動到塔c,最後把放在塔b的(圓盤n-1~圓盤1)從塔b移動到塔c。
可以看出,上面的解法是很典型的分治法演算法思想。
因為漢諾塔要求一次只能移動乙個圓盤,並且小圓盤上面不能放大圓盤,所以需要把同乙個塔最底下的大圓盤上面的圓盤搬走。要想移動圓盤n,那麼先要移動其上的(圓盤n-1~ 圓盤1)到中轉塔,要想移動(圓盤n-1~ 圓盤1)中的圓盤n-1,那麼先要移動其上的(圓盤n-2~ 圓盤1)到中轉塔,依次類推……直到只有乙個圓盤1,那麼直接移動即可。同時,因為我們的目的是把所有的圓盤移動到目標塔,所以在移動完圓盤n到目標塔之後,需要把之前放在中轉塔上的(圓盤n-1~圓盤1)移動到目標塔。
可能大家會說了,道理都懂,但是**該如何寫呢?其實這也是演算法學習中比較佔時間的部分,用計算機程式語言把演算法表示(寫)出來。下面給大家附上經典的實現,分析為何要這樣寫,並介紹一種我自己平時理解遞迴所用的思考方法。
一般來說,分治法總是結合遞迴去實現,以下示例**就用了遞迴結構。
在寫遞迴結構的時候,可以從語義層面去出發。思考過程如上面分析所示:我們需要寫乙個函式,它的作用是把圓盤n~圓盤1由乙個起始塔移動到目標塔,由於要求要先移動圓盤n上面的圓盤後才能移動圓盤n,所以需要乙個中轉塔。經過這樣的思考過程之後,我們就可以確定函式的簽名了,即void hanoi(int n, string sourcetower, string temptower, string targettower)
。
根據演算法,函式體裡面就可這樣寫(語義層面):
如果當前為盤子1,那麼直接移動到目標塔move(n, sourcetower, targettower)
;
否則先將圓盤n-1~圓盤1移動到中轉塔hanoi(n - 1, sourcetower, targettower, temptower)
,移動完之後把圓盤n先移動到目標塔move(n, sourcetower, targettower)
,再把中轉塔上的圓盤n-1~圓盤1移動到目標塔hanoi(n - 1, temptower, sourcetower, targettower)
。
public
class
hanoi
else
}//盤子n的從sourcetower->targettower的移動
private
static
void
move
(int n, string sourcetower, string targettower)
public
static
void
main
(string[
] args)
}
執行結果:
移動漢諾塔的步驟:
第1號盤子 move:a--->c
第2號盤子 move:a--->b
第1號盤子 move:c--->b
第3號盤子 move:a--->c
第1號盤子 move:b--->a
第2號盤子 move:b--->c
第1號盤子 move:a--->c
可以發現,對於一次呼叫hanoi(n, sourcetower, temptower, targettower)
,中轉塔是給圓盤n-1~圓盤1用的。從語義上理解,hanoi(n, sourcetower, temptower, targettower)
最終的呼叫效果是圓盤n~圓盤1從起始塔全部移動到了目標塔,並按要求(在小圓盤上不能放大圓盤)所有圓盤都放在目標塔上了。可能有些同學還是不能理解為什麼滿足了在小圓盤上不能放大圓盤的的這個要求,很簡單,因為我們每次想移動圓盤n的時候,都已經把圓盤n上面的圓盤拿走了,移動完圓盤n之後,再把其他圓盤移動到圓盤n所在的塔上,所以當然是滿足了在小圓盤上不能放大圓盤的的這個要求。如果還是不能明白的話,可以從2個圓盤的情況和3個圓盤的情況去模擬看看。
附上乙個演算法呼叫的分析圖:
平時在用分治法解決問題的時候,先根據分治法的原則把問題逐步拆分。而分治法往往需要和遞迴結合起來去寫**,閱讀或者設計遞迴結構的時候可以從語義的層面出發去進行。
與分治法一樣,回溯法也可以跟遞迴結合去實現。但是兩者還是有些許區別的。
分治法使用遞迴解決問題,問題一般是有解的。如漢諾塔,鍊錶反轉。
回溯法使用遞迴試探問題的解法,可能會無解。如迷宮問題,深度遍歷。
其實鑽研演算法的確挺燒腦的,而且需要投入比較多的時間。很多人覺得有這時間還不如去看看別的框架更好。其實吧,如果想在計算機領域成為大牛,演算法知識是必不可少的。本人很喜歡看動漫《一拳超人》,裡面有一句台詞「興趣使然」。確實,我覺得演算法是最能體現電腦科學之魅力的東西,所以研究演算法就不會覺得無聊,因為「興趣使然」。
——寫於2018中秋佳節。
漢諾塔問題(遞迴)
題目描述 對於傳統的漢諾塔遊戲我們做乙個拓展,我們有從大到小放置的n個圓盤,開始時所有圓盤都放在左邊的柱子上,按照漢諾塔遊戲的要求我們要把所有的圓盤都移到右邊的柱子上,請實現乙個函式列印最優移動軌跡。給定乙個int n,表示有n個圓盤。請返回乙個string陣列,其中的元素依次為每次移動的描述。描述...
漢諾塔問題(遞迴)
問題 漢諾塔問題 解法 遞迴求解 思路 先把n 1從a移動b 在把第n個從a移到c 使用遞迴使得 變得簡單 複雜度 2的n次方 1 includeint step 1 void hanoi int level,char a,char b,char c 1 當盤子數大於1時,先把n 1個從a借助c移動...
漢諾塔問題(遞迴)
漢諾塔 在印度,有這麼乙個古老的傳說 在世界中心貝拿勒斯 在印度北部 的聖廟裡,有三根柱子。印度教的主神梵天在創造世界的時候,在其中一根柱子上從下到上地穿好了由大到小的64片金盤,這就是所謂的漢諾塔。不論白天黑夜,總有乙個僧侶在按照下面的法則移動這些金盤 一次只移動一片,不管在哪根柱子上,小片必須在...