遞迴實現漢諾塔問題
雖然搞程式多年了,對遞迴演算法還是有些打怵。遞迴本身好理解,但其各層巢狀卻容易將人繞暈,遞迴的漢諾塔問題就將我搞暈了多次。我搜了好多資料,也查閱了好多書籍,但都是泛泛而談,不夠詳細,下面是我精心總結一下漢諾塔問題。
漢諾塔的問題:(百度百科引用)
漢諾塔問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。
或許下面的解釋會好理解一些:
現在要求廟裡的老和尚把這64個盤子全部移動到第三個柱子上。移動的時候始終只能小盤子壓著大盤子。
1、此時老和尚(後面我們叫他第1個和尚)覺得很難,所以他想:要是有一個人能把前63個盤子先移動到第二個柱子上,我再把最後一個盤子直接移動到第三個柱子,再讓那個人把剛才的前63個盤子從第二個柱子上移動到第三個柱子上,我的任務就完成了,簡單。所以他找了比他年輕的和尚(後面我們叫他第2個和尚)(呵呵,倚老賣老),命令:
①你把前63個盤子移動到第二柱子上
②在我自己把第64個盤子一道第三個柱子上後
③你把前63個盤子移動到第三柱子上
2、第2個和尚接了任務,也覺得很難,所以他也和第1個和尚一樣想:要是有一個人能把前62個盤子先移動到第三個柱子上,我再把最後一個盤子直接移動到第二個柱子,再讓那個人把剛才的前62
①你把前62個盤子移動到第三柱子上
②在我自己把第63個盤子一道第二個柱子上後
③你把前62個盤子移動到第二柱子上
3、第3個和尚接了任務,又把移動前61個盤子的任務依葫蘆話瓢的交給了第4個和尚,等等遞推下去,直到把任務交給了第64個和尚為止(估計第64個和尚很鬱悶,沒機會也命令下別人,因為到他這裡盤子已經只有一個了)。
4、到此任務下交完成,到各司其職完成的時候了。
完成回推了:
第64個和尚移動第1個盤子,把它移開,然後第63個和尚移動他給自己分
配的第2個盤子。第64個和尚再把第1個盤子移動到第2個盤子上。到這裡第64個和尚的任務完成,第63個和尚完成了第62個和尚交給他的任務的第一步。
從上面可以看出,只有第64個和尚的任務完成了,第63個和尚的任務才能完成,只有第2個和尚—第64個和尚的任務完成後,第1個和尚的任務才能完成。這是一個典型的遞迴問題。
現在我們以有3個盤子來分析:
第1個和尚命令:
㈠第2個和尚你先把第一柱子前2個盤子移動到第二柱子。(藉助第三個柱子)
㈡第1個和尚我自己把第一柱子最後的盤子移動到第三柱子。
㈢第2個和尚你把前2個盤子從第二柱子移動到第三柱子。
很顯然,第㈡步很容易實現(哎,人總是自私地,把簡單留給自己,困難的給別人)
其中第㈠步。第2個和尚他有2個盤子,他就命令:
①第3個和尚你把第一柱子第1個盤子移動到第三柱子。(藉助第二柱子)
②第2個和尚我自己把第一柱子第2個盤子移動到第二柱子上。
③第3個和尚你把第1個盤子從第三柱子移動到第二柱子。
同樣,第步很容易實現,但第3個和尚他只需要移動1個盤子,所以他也不用在下派任務了。(注意:這就是停止遞迴的條件,也叫邊界值)
第㈢步可以分解為,第2個和尚還是有2個盤子,命令:
①第3個和尚你把第二柱子上的第1個盤子移動到第一柱子。
②第2個和尚我把第2個盤子從第二柱子移動到第三柱子。
③第3個和尚你把第一柱子上的盤子移動到第三柱子。
分析組合起來就是:1→3 1→2 3→2 1→3 2→1 2→3 1→3共需要七步。如果是4個盤子,則第一個和尚的命令中第1步和第3步各有3個盤子,所以各需要7步,共14步,再加上第1個和尚的1步,所以4個盤子總共需要移動7+1+7=15步,同樣,5個盤子需要15+1+15=31步,6個盤子需要31+1+31=64步……由此可以知道,移動n個盤子需要(2的n次方)--1步。
從上面整體綜合分析可知把n個盤子從1座(相當第一柱子)移到3座(相當第三柱子):
(1)把1座上(n-1)個盤子藉助3座移到2座。
(2)把1座上第n個盤子移動3座。
(3)把2座上(n-1)個盤子藉助1座移動3座。
下面用hanoi(n,a,b,c)表示把1座n個盤子藉助2座移動到3座。
很明顯,(1)步上是 hanoi(n-1,1,3,2)
(2)步上是hanoi(n-1,2,1,3)
下面是實現程式碼:
//漢諾塔問題
#include <iostream>
using namespace std;
//將編號為numdisk的盤子從init杆移至desti杆
void moveOne(int numDisk, string init, string desti)
{
cout << "Move disk No. " << numDisk << " from " << init << " to " << desti << endl;
}
//將numDisks個盤子從init杆藉助temp杆移至desti杆
void move(int numDisks, string init, string temp, string desti)
{
if(numDisks == 1)
moveOne(1, init, desti);
else
{
move(numDisks-1, init, desti, temp);//首先將上面的(numDisk-1)個盤子從init杆藉助desti杆移至temp杆
moveOne(numDisks, init, desti); //然後將編號為numDisks的盤子從init杆移至desti杆
move(numDisks-1, temp, init, desti);//最後將上面的(numDisks-1)個盤子從temp杆藉助init杆移至desti杆
}
}
int main()
{
move(3, "A", "B", "C");
return 0;
}
/*
//===============執行結果==================
Move disk No. 1 from A to C
Move disk No. 2 from A to B
Move disk No. 1 from C to B
Move disk No. 3 from A to C
Move disk No. 1 from B to A
Move disk No. 2 from B to C
Move disk No. 1 from A to C
請按任意鍵繼續. . .
*/
下面構造Hanoi類
//漢諾塔問題
#include <iostream>
using namespace std;
class Hanoi
{
public:
Hanoi(int disks):totalDisks(disks){};
void solve();
private:
int totalDisks;
void move(int numDisks, string init, string temp, string desti);
void moveOne(int numDisk, string init, string desti);
};
void Hanoi::solve()
{
string init = "A";
string temp = "B";
string desti = "C";
move(totalDisks, init , temp, desti);
}
//將編號為numdisk的盤子從init杆移至desti杆
void Hanoi::moveOne(int numDisk, string init, string desti)
{
cout << "Move disk No. " << numDisk << " from " << init << " to " << desti << endl;
}
//將numDisks個盤子從init杆藉助temp杆移至desti杆
void Hanoi::move(int numDisks, string init, string temp, string desti)
{
if(numDisks == 1)
moveOne(1, init, desti);
else
{
move(numDisks-1, init, desti, temp);//首先將上面的(numDisk-1)個盤子從init杆藉助desti杆移至temp杆
moveOne(numDisks, init, desti); //然後將編號為numDisks的盤子從init杆移至desti杆
move(numDisks-1, temp, init, desti);//最後將上面的(numDisks-1)個盤子從temp杆藉助init杆移至desti杆
}
}
int main()
{
Hanoi hanoi(3);
hanoi.solve();
return 0;
}
/*
//===============執行結果==================
Move disk No. 1 from A to C
Move disk No. 2 from A to B
Move disk No. 1 from C to B
Move disk No. 3 from A to C
Move disk No. 1 from B to A
Move disk No. 2 from B to C
Move disk No. 1 from A to C
請按任意鍵繼續. . .
*/
如轉載請註明出處:http://blog.csdn.net/rehongchen/article/details/7981570