遞迴演算法經典例項小結(C#實現)

雲霏霏發表於2015-02-04
 一 、遞迴演算法簡介

在數學與電腦科學中,遞迴是指在函式的定義中使用函式自身的方法。
  遞迴演算法是一種直接或者間接地呼叫自身演算法的過程。在計算機編寫程式中,遞迴演算法對解決一大類問題是十分有效的,它往往使演算法的描述簡潔而且易於理解。
遞迴演算法解決問題的特點:
  (1) 遞迴就是在過程或函式裡呼叫自身。
  (2) 在使用遞迴策略時,必須有一個明確的遞迴結束條件,稱為遞迴出口。
  (3) 遞迴演算法解題通常顯得很簡潔,但遞迴演算法解題的執行效率較低。所以一般不提倡用遞迴演算法設計程式。
  (4) 在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存。遞迴次數過多容易造成棧溢位等。所以一般不提倡用遞迴演算法設計程式。在實際程式設計中尤其要注意棧溢位問題。

  藉助遞迴方法,我們可以把一個相對複雜的問題轉化為一個與原問題相似的規模較小的問題來求解,遞迴方法只需少量的程式就可描述出解題過程所需要的多次重複計算,大大地減少了程式的程式碼量。但在帶來便捷的同時,也會有一些缺點,也即:通常用遞迴方法的執行效率不高。

 

 二 、Fibonacci數列和階乘

1、 Fibonacci數列

提到遞迴,我們可能會想到的一個例項便是斐波那契數列。斐波那契數列就是如下的數列:

  0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …,總之,就是第N(N > 2)個數等於第(N - 1)個數和(N - 2)個數的和。用遞迴演算法實現如下:

 public static int Fibonacci(int n)
      {
         if (n < 0) return -1;
         if (n == 0) return 0;
         if (n == 1) return 1;
         return Fibonacci(n - 1) + Fibonacci(n - 2);
      }

 

2、階乘 

還有就是求一個數的階乘,也會用到遞迴,這個比較簡單,直接給出實現程式碼,如圖:

 

 三 、漢諾塔問題

 漢諾塔是根據一個傳說形成的數學問題:

        漢諾塔示意圖(圖片來自網路)

 有三根杆子A,B,C。A杆上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至C杆:
  1、每次只能移動一個圓盤;
  2、大盤不能疊在小盤上面。
  提示:可將圓盤臨時置於B杆,也可將從A杆移出的圓盤重新移回A杆,但都必須遵循上述兩條規則。
  問:如何移?最少要移動多少次?

 下面是漢諾塔的遞迴求解實現(C#程式碼):

public static void hannoi(int n, string from, string buffer, string to)
      {
         if (n == 1)
         {
            Console.WriteLine("Move disk " + n + " from " + from + " to " + to);
         }
         else
         {
            hannoi(n - 1, from, to, buffer);
            Console.WriteLine("Move disk " + n + " from " + from + " to " + to);
            hannoi(n - 1, buffer, from, to);
         }
      }

其執行結果如圖(大家可以跟上面的gif圖片對比一下):

 

 四 、排列組合

1、輸出任意個數字母、數字的全排列 

  對於一個長度為n的串或者n個字元(數字、節點)組成的字串陣列,它的全排列共有A(n, n)=n!種。這個問題也是一個遞迴的問題。如1,2,3,全排列可得到:{123,132,213,231,312,321}。

用遞迴演算法實現程式碼如下:

public static void Permutation(string[] nums, int m, int n)
      {
         string t;
         if (m < n - 1)
         {
            Permutation(nums, m + 1, n);
            for (int i = m + 1; i < n; i++)
            {
               //可抽取Swap方法
               t = nums[m];
               nums[m] = nums[i];
               nums[i] = t;
               
               Permutation(nums, m + 1, n);

               //可抽取Swap方法
               t = nums[m];
               nums[m] = nums[i];
               nums[i] = t;
            }
         }
         else
         {for (int j = 0; j < nums.Length; j++)
            {
               Console.Write(nums[j]);
            }
            Console.WriteLine();
         }
      }

呼叫程式碼如下:

static void Main(string[] args)
      {
         Nums = new string[] { "a", "b", "c" };
         Permutation(Nums, 0, Nums.Length);
         Console.ReadKey();
      }

 這裡傳入一個string陣列,abc三個字母來測試,輸出如下圖:

 

2、將全排列結果儲存到連結串列中

  有時候,我們需要將全排列的結果儲存,然後做其他的處理,我們可以將結果儲存到一個連結串列中。我們定義如下類作為連結串列的節點,程式碼如下:

   public class Node
   {
      public string value { get; set; }
      public Node nextNode { get; set; }

      public Node(string value)
      {
         this.value = value;
         this.nextNode = null;
      }
   }

 此時宣告全域性變數,如下:

public static List<Node> NodeList = new List<Node>();

這個時候,我們修改Permutation方法,如下:

  public static void Permutation(string[] nums, int m, int n)
      {
         string t;
         if (m < n - 1)
         {
            Permutation(nums, m + 1, n);
            for (int i = m + 1; i < n; i++)
            {
               //可抽取Swap方法
               t = nums[m];
               nums[m] = nums[i];
               nums[i] = t;
               
               Permutation(nums, m + 1, n);

               //可抽取Swap方法
               t = nums[m];
               nums[m] = nums[i];
               nums[i] = t;
            }
         }
         else
         {
            Node root = null;
            Node currentNode;
            for (int j = 0; j < nums.Length; j++)
            {
               currentNode = new Node(nums[j]);
               currentNode.nextNode = root;
               root = currentNode;
            }
            NodeList.Add(root);
         }
      }

這樣,我們執行了Permutation方法後,就將結果儲存到連結串列中了。用的時候,我們只要遍歷NodeList就可以了。如圖:

 遞迴演算法就先說到這裡了。談到演算法,就必需提資料結構,看來真的要“學到老了”~~

 

 作者:雲霏霏

QQ交流群:243633526

 部落格地址:http://www.cnblogs.com/yunfeifei/

 宣告:本部落格原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未授權,貼子請以現狀保留,轉載時必須保留此段宣告,且在文章頁面明顯位置給出原文連線。

如果大家感覺我的博文對大家有幫助,請推薦支援一把,給我寫作的動力。

 

相關文章