目的:領會基本遞歸算法設計和遞歸到非遞歸的轉換方法
內容:編寫一個程序exp5-1.cpp,采用遞歸和非遞歸方法求解Hanoi問題,輸出三個盤片的移動過程
寫在前面
題目是昨天老師發在學習通上的,目前
解決了:
- Hanoi問題理解
- Hanoi遞歸算法及其實現
未解決的:
- Hanoi非遞歸算法及實現
(PPT上是用棧來實現的,然而筆者對棧不熟...解決完再記上來吧)
以下就從解決了的兩個方面展開討論
Hanoi問題理解
筆者參考的是算法動態圖解app上的解釋,還挺直觀的:
簡單來說就是有X,Y,Z三個柱子,目標是將X柱子上的盤片移動到Y柱子上,同時要保持和X柱相同的順序。X柱子上盤片自下而上、由大到小放置着n個盤片,兩個要求:
Ⅰ. 一次只能移一個盤片
Ⅱ.不能把大盤片放在小盤片上
個人的理解:
首先,為了方便分析,將X柱上盤片自上而下依次編號為\(1,2...n\)
- 當n=1時,即X柱上只有一個盤片,直接從X移動到Z,這是最簡單的情況
- 當n=2 時,
第一步,把1號移動到Y柱(因為要滿足要求Ⅱ,2號盤片必須第一個移動到Z柱上,那么必須把1號先移開,移動到哪里?肯定不能是Z柱,那么就是Y柱了)
第二步,把2號盤移到Z柱
第三步,把1號從Y柱移動到Z柱,完成! - 當n=3時,
第一步,可以把1號,2號盤,通過Z柱移到Y柱,這在前面(n=2時)已經證明可行。
第二步,把3號盤移到Y柱
第三步,發現問題轉化為n=2的情況,即把1號,2號盤,從Y柱移動到Z柱,方法參見n=2,不再贅述
寫到這里,我們發現這個問題是可遞歸的,n個盤片的Hanoi問題可以通過n-1個盤片的Hanoi問題求得,抽象成數學語言就是$$f(n)=g(f(n-1),c_{n-1},
,f(1)=m_1$$
要求\(f(n)\),可先求\(f(n-1),f(n-2)...\),層層遞歸,直到\(f(1)\)
Hanoi遞歸算法及其實現
遞歸模型及遞歸算法
- 設計遞歸模型
可以看出遞歸模型分為遞歸體,遞歸出口兩個部分,其實就是高中數學數列,遞歸函數概念 - 設計遞歸算法(以本題為例)
把盤片數,X柱,Y柱,Z柱分別抽象成自定義函數的四個參數n,X,Y,Z
void Hanoi1(int n,char X,char Y,char Z)
分別用if語句,else語句描述遞歸出口與遞歸體
- 算法代碼:
//遞歸法
void Hanoi1(int n,char X,char Y,char Z){
if(n==1){
printf("\t將第%d個盤片從%c移動到%c\n",n,X,Z);//遞歸出口,當只有一個盤片時 }
else {
Hanoi1 (n-1,X,Z,Y);//將n-1個盤片,通過z,從x移動到y
printf ("\t把第%d個盤片從%c移動到%c\n",n,X,Z);//將第n個盤片,從x移動到z
Hanoi1 (n-1,Y,X,Z);//將n-1個盤片,通過x,從y移動到z
}
}
Hanoi遞歸算法的實現
理解了算法代碼的實現,接下來就就是"搬磚"階段了
在main()函數里分別初始化三個參數的值,然后調用Hanoi1()就可以了
完整代碼:
#include <iostream>
using namespace std;
//遞歸法
void Hanoi1(int n,char X,char Y,char Z){
if(n==1){
printf("\t將第%d個盤片從%c移動到%c\n",n,X,Z);//遞歸出口,當只有一個盤片時
}
else {
Hanoi1 (n-1,X,Z,Y);//將n-1個盤片,通過z,從x移動到y
printf ("\t把第%d個盤片從%c移動到%c\n",n,X,Z);//將第n個盤片,從x移動到z
Hanoi1 (n-1,Y,X,Z);//將n-1個盤片,通過x,從y移動到z
}
}
int main() {
int n=3;
char X='x',Y='y',Z='z';
cout <<"遞歸法過程:"<<endl;
Hanoi1 (n,X,Y,Z);
return 0;
}
運行截圖:
寫在最后
在"搬磚"的過程中,出現了許多錯誤,變量未初始化(uninitialzing),函數未指名(not declared) ...原因在於沒有經歷完整的c語言編程過程,只是看教材的代碼,效率確實太低了。