1. 程式人生 > >(優秀漢諾塔演算法)對漢諾塔經典遞迴問題的理解與講解(部分引用大神程式碼,附連結。)

(優秀漢諾塔演算法)對漢諾塔經典遞迴問題的理解與講解(部分引用大神程式碼,附連結。)

部落格大神的優秀漢諾塔程式碼:喜歡特別冷的冬天下著雪   (侵權聯絡)

本文只是在大神思路的基礎上加以理解。

[cpp] view plain copy  print?
  1. #include <stdio.h>
  2. //第一個塔為初始塔,中間的塔為借用塔,最後一個塔為目標塔
  3. int i=1;//記錄步數
  4. void move(int n,char from,char to) //將編號為n的盤子由from移動到to
  5. {printf("第%d步:將%d號盤子%c---->%c\n",i++,n,from,to);  
  6. }  
  7. void hanoi(int n,char from,char
     denpend_on,char to)//將n個盤子由初始塔移動到目標塔(利用借用塔)
  8. {  
  9.     if (n==1)  
  10.     move(1,from,to);//只有一個盤子是直接將初塔上的盤子移動到目的地
  11.     else
  12.     {  
  13.       hanoi(n-1,from,to,denpend_on);//先將初始塔的前n-1個盤子藉助目的塔移動到借用塔上
  14.       move(n,from,to);              //將剩下的一個盤子移動到目的塔上
  15.       hanoi(n-1,denpend_on,from,to);//最後將借用塔上的n-1個盤子移動到目的塔上
  16.     }  
  17. }  
  18. void main()  
  19. {  
  20.      printf("請輸入盤子的個數:\n");  
  21.      int n;  
  22.      scanf("%d",&n);  
  23.      char x='A',y='B',z='C';  
  24.      printf("盤子移動情況如下:\n");  
  25.      hanoi(n,x,y,z);  
  26. }  

大神的程式碼很簡明,即涉及到了計數,又完美的凸顯了整個過程細節,但是對於部分人快速理解來說有些困難。

在遞迴的過程中,重點在於對第n步與第n-1步之間的關係分析,最基本的就是數學關係:例如f(n)=f(n-1)+f(n-2)、f(n)=f(n-1)*m;

確定了準確的遞迴關係之後,就需要我們根據程式功能來設定基本值了:例如Fibonacci  就可設定 f(1)=0,f(2)=1;再使用if  等語句來判斷條件的達到,從而終止遞迴,返回上層函式,最後返回答案。

遞迴問題中,在具體的兩步之間研究出其中的規律就能遞迴下去

在漢諾塔中,基本的數學關係特別簡單,就是將第n個(此處假設越往下盤子號數越大)盤子放到目的塔之前,要將上面的n-1個盤子先放到借用塔(步驟1)上,將第n個盤子放到目的塔上(步驟2)後,再將這n-1個盤子從借用塔放到目的塔(步驟3)

當移動次數用函式fun(n)表示時  其中,數量關係是:

步驟1與步驟3移動次數其實是一樣的,因為都是移動n-1個盤子到另外一個塔上。

而步驟2只需要一次移動;

所以:f(n)=2*f(n-1)+1;

但是,如果需要明確的看清每一次移動是怎麼做到的就必須輸出每一步的移動過程,這不是知道簡單的數學關係就能解決的,

這裡就需要用到,漢諾塔問題遞迴中存在的另外一種關係:

三座塔:初始塔。目的塔。借用塔的身份轉換關係:

具體到f(n)初始塔 是 A   目的塔是C  借用塔是B

對於步驟1:進入到f(n-1)  此時的初始塔依舊是A  但  目的塔變為 B 借用塔變為 C  (不太清楚的話  畫畫圖)

因此這裡的遞迴變化在於   目的塔與借用塔身份互換。

對於步驟2:不需要借用塔,  原來的目的塔與初始塔也無變化。 

對於步驟3:進入到f(n-1) ' 此時的初始塔變為B  但  目的塔變為C 借用塔變為 A (不太清楚的話  畫畫圖)

因此這裡的遞迴變化在於  上面所述的變化。

對於之後的每一次簡化遞迴 上層函式對他下面的函式來說  都是這種塔的身份變換。

因此,我們可以利用這種塔的變換來進行輸出中的呼叫,將三個塔柱命名,通過傳參,將每一步正確的移動細節輸出。

  1. void move(int n,char from,char to) //將編號為n的盤子由from移動到to
  2. {printf("第%d步:將%d號盤子%c---->%c\n",i++,n,from,to);  
  3. }  
並且  通過i++來記錄一共需要多少步。

去掉那層輸出後,就可以看做單純的計數程式:

#include <stdio.h>  
//第一個塔為初始塔,中間的塔為借用塔,最後一個塔為目標塔  
int i=1;//記錄步數  
void move(int n) //將編號為n的盤子由from移動到to  
{printf("第%d步",i++);  
}  
void hanoi(int n)//將n個盤子由初始塔移動到目標塔(利用借用塔)  
{  
    if (n==1)  
    move(1);//只有一個盤子是直接將初塔上的盤子移動到目的地  
    else  
    {  
      hanoi(n-1);//先將初始塔的前n-1個盤子藉助目的塔移動到借用塔上  
      move(n);              //將剩下的一個盤子移動到目的塔上  
      hanoi(n-1);//最後將借用塔上的n-1個盤子移動到目的塔上  
    }  
}  
int main()  
{  
     printf("請輸入盤子的個數:\n");  
     int n;  
     scanf("%d",&n);  
     char x='A',y='B',z='C';  
     printf("盤子移動情況如下:\n");  
     hanoi(n);  
     return 0;
}