1. 程式人生 > 其它 >遞迴分治 --- 演算法思想介紹

遞迴分治 --- 演算法思想介紹

一.遞迴分治的基本概念

遞迴的概念:直接或間接的呼叫自身的演算法稱為遞迴演算法.用函式自身給出定義的函式成為遞迴函式.
分治法的思想:將一個難以直接解決的大問題分割成一些規模較小的相同問題,以便各個擊破,即分而治之.

如果原問題可分割成k個子問題, 1<k<=n, 且這些子問題都可解,並可利用這些子問題的解求出原問題的解,那麼這種分治法就是可行的.有分治法產生的子問題往往是原問題的較小模式,這為使用遞迴技術提供了方便.在這種情況下,反覆利用分治手段,可以使子問題與原問題型別一致而其規模不斷縮小,最終是子問題縮小到容易求出其解,由此自然引出遞迴演算法.

分治與遞迴像一對孿生兄弟,經常同時應用在演算法設計中,並由此產生許多高效演算法.

二.遞迴分治演算法的適用條件

我們下面利用幾個例子,來看一看遞迴分治演算法什麼情況下能夠使用,以及應該如何使用.

從我們耳熟能詳的階乘函式和斐波那契數列開始,介紹一下遞迴思想:
階乘函式:可遞迴地定義為:

  • n! = 0, n = 1 (此為邊界條件)
  • n! = n*(n-1)!, n > 0 (此為遞迴方程)

邊界條件與遞迴方程是遞迴函式的二個要素,遞迴函式只有具備了這兩個要素,才能在有限次計算後得出結果.

int Factorial(int n)
{
	if(n == 0) return 1;
	else return n*Factorial(n-1);
}

Fibonacci數列:無窮數列1,1,2,3,5,8,13,21,34,55,……,稱為Fibonacci數列。它可以遞迴地定義為:

  • F(n) = 0, n = 0
  • F(n) = 1, n = 1
  • F(n) = F(n-1) + F(n-2), n > 1
int fibonacci(int n)
 {
     if (n <= 1) return 1;
     return fibonacci(n-1)+fibonacci(n-2);
 }

階乘函式和Fibonacci數列這兩種遞迴可轉換為非遞迴方式,但並不是所有遞迴都可以轉換.

認識了簡單的遞迴之後,我們舉一個分治演算法的例子 --- 二分查詢:
已知不重複且從小到大排列的m個整數的陣列A[1...m],要求找到一個下標i,使得A[i] = x, 找不到返回0;

基本思想:
將m個元素分成個數大致相同的兩半,取A[mid]與x作比較。

  • x = A[mid], 演算法終止
  • x < A[mid], 在陣列的左半部繼續搜尋
  • x > A[mid], 在陣列的右半部繼續搜尋
int BinarySearch(int A[], int x, int l, int r)
{
	while(l <= r)
	{
		int mid = (l+r) / 2;
		if(A[mid] == x) return mid;
		else if(x < A[mid])
			r = mid - 1;		
		else
			l = mid + 1;
	}
	return 0;
}

分治法的適用條件(其實就相當於是遞迴演算法的適用條件了,因為分治法實現起來大部分是使用了遞迴):

  • 該問題的規模縮小到一定的程度就可以容易地解決;
  • 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質
  • 利用該問題分解出的子問題的解可以合併為該問題的解
  • 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子問題

最後一條這條特徵涉及到分治法的效率,如果各子問題是不獨立的,則分治法要做許多不必要的工作,重複地解公共的子問題,此時雖然也可用分治法,但一般用動態規劃較好.

注意:人們從大量實踐中發現,在用分治法設計演算法時,最好使子問題的規模大致相同。即將一個問題分成大小相等的k個子問題的處理方法是行之有效的。這種使子問題規模大致相等的做法是出自一種平衡(balancing)子問題的思想,它幾乎總是比子問題規模不等的做法要好.

三.遞迴分治演算法總結

遞迴分治演算法的模板虛擬碼:

Divide-and-Conquer(P)
	if(|P|<=n0) Adhoc(P)   //若問題規模小於閾值,直接計算   
	divide P into smaller subinstances P1, P2, ... , Pk   //否則,將大問題劃分為若干個小問題
	for(i = 1; i<= k; i++)		//依次計算每個小問題
		yi = Divide-and-Conquer(Pi); 
	return Merge(y1, y2, ... , yk);	//合併每個小問題的解,得到最終答案

設問題P(n)分解成k個規模為n/m的子問題,閥值n0=1,求解P(1)的時間耗費為O(1).將P(n)分解及合併成P(n)的解的時間為f(n),則分治法解規模為n的問題的最壞時間複雜性函式T(n)滿足:

  • T(n) = T(1) = O(1)
  • ​ = T(n) = kT(n/m) + f(n)

答案很複雜,總之遞迴演算法對時間空間複雜度要求較高.
後面我會針對具體的幾個演算法題來應用遞迴分治思想解決,歡迎繼續閱讀.

參考畢方明老師《演算法設計與分析》課件.

如果覺得本篇文章對你有所幫助,歡迎大家來到我的個人部落格網站---喬治的程式設計小屋逛一逛吧.