首先我們要考慮使用遞歸的2個條件,原問題是否可以分解為形式相同但規模更小的問題,還有就是如果存在這樣的分解,那么這種分解是否存在一種簡單情境?
先來看第一點,是否存在一種符合條件的分解。容易發現,如果一個字符串是回文,那么在它的內部一定存在着更小的回文。 比如level里面的eve也是回文。 而且,我們注意到,一個回文的第一個字符和最后一個字符一定是相同的。所以我們很自然的有這樣的方法:先判斷給定字符串的首尾字符是否相等,若相等,則判斷去掉首尾字符后的字符串是否為回文,若不相等,則該字符串不是回文。 注意,我們已經成功地把問題的規模縮小了,去掉首尾字符的字符串當然比原字符串小。
接着再來看第二點, 這種分解是否存在一種簡單情境呢?簡單情境在使用遞歸的時候是必須的,否則你的遞歸程序可能會進入無止境的調用。對於回文問題,我們容易發現,一個只有一個字符的字符串一定是回文,所以,只有一個字符是一個簡單情境,但它不是唯一的簡單情境,因為空字符串也是回文。這樣,我們就得到了回文問題的兩個簡單情境:字符數為1和字符數為0。
好了,兩個條件都滿足了,基於以上分析,我們可以很容易的編寫出解決回文問題的遞歸實現方式,
剛開始寫的代碼是這樣:
int isPalindrome(char *s,int n) { if(n==0 || n==1) return 1; else { return ((s[0]==s[n-1]))?isPalindrome(s+1,n-1):0; } }
這樣的代碼對嗎?輸入"aba",會發現輸出的是0,不是1.
說明代碼錯誤為什么?首先func(aba,3)->func(ba,2)
s[0]=b, s[1]=a;//明顯不相等,返回0.
從上面的函數調用過程我們可以看出為什么錯了?原因在於
每次比較后,n應該是減2而不應該是減1.(去掉了2個字符)
從上面錯誤的代碼中,我們稍微改下寫成如下的代碼:
int isPalindrome(char *s,int n) { if(n==0 || n==1) return 1; else { return ((s[0]==s[n-1]))?isPalindrome(s+1,n-2):0; } }
程序正確。
還可以這么寫:
int isPalindrome2(char *s,int b,int e) { if(b==e || b>e) { return 1; } else { return s[b]==s[e]?isPalindrome2(s,b+1,e-1):0; } }
注意base case:為奇數時b==e,偶數時b>e.
這種方式更好理解。
還有一個典型的例子是對已排序數組的二分查找算法。
現在在有一個已經排序好的數組,要在這個數組中查找一個元素,以確定它是否在這個數組中,很一般的想法是順序檢查每個元素,看它是否與待查找元素相同。這個方法很容易想到,但它的效率不能讓人滿意,它的復雜度是O(n)的。現在我們來看看遞歸在這里能不能更有效。
還是考慮上面的兩個條件: 第一:這個問題是否可以分解為形式相同但規模更小的問題?第二:如果存在這樣一種分解,那么這種分解是否存在一種簡單情境?
考慮條件一:我們可以這樣想,如果想把問題的規模縮小,我們應該做什么?可以的做法是:我們先確定數組中的某些元素與待查元素不同,然后再在剩下的元素中查找,這樣就縮小了問題的規模。那么如何確定數組中的某些元素與待查元素不同呢? 考慮到我們的數組是已經排序的,我們可以通過比較數組的中值元素和待查元素來確定待查元素是在數組的前半段還是后半段。這樣我們就得到了一種把問題規模縮小的方法。
接着考慮條件二:簡單情境是什么呢? 容易發現,如果中值元素和待查元素相等,就可以確定待查元素是否在數組中了,這是一種簡單情境,那么它是不是唯一的簡單情境呢? 考慮元素始終不與中值元素相等,那么我們最終可能得到了一個無法再分的小規模的數組,它只有一個元素,那么我們就可以通過比較這個元素和待查元素來確定最后的結果。這也是一種簡單情境。
好了,基於以上的分析,我們發現這個問題可以用遞歸來解決,二分法的代碼如下:
int binarySearch2(int * a, int n, int key) { int mid; if(n == 1){ return (a[0] == key); }else{ mid = n/2; if(a[mid-1] == key) return 1; else if(a[mid-1] > key) return binarySearch2(a, mid, key); else return binarySearch2(a[mid], n - mid, key); } }
這個算法的復雜度是O(logn)的,顯然要優於先前提到的朴素的順序查找法。
好了,今天就說到這里,光看不管用,不如留個練習題您回去做做:
寫一個函數dig sum(int n),輸入一個非負整數,返回組成它的數字之和。如:digsum(2007) = 2+0+0+7 = 9。
參考:
http://www.pureweber.com/article/recursive-power-2/
我的代碼:
#include<iostream> using namespace std; int binarySearch(int a[],int b,int e,int key) { if(b>e) { return -1; } int mid=(b+e)/2; if(a[mid]==key) return mid; else if(a[mid]<key) return binarySearch(a,mid+1,e,key); else return binarySearch(a,b,mid-1,key); } int main() { int a[]={1,3,4,5}; cout<<binarySearch(a,0,(sizeof(a)/sizeof(a[0]))-1,0); }