遞歸

遞歸---Recursion

在學習清華大學鄧俊輝鄧公的數據結構這門課中,鄧公引用了這樣一句話:html

To iterate is human, to recurse, divine. (迭代乃人工,遞歸方神通。)算法

足見遞歸算法的重要性。數組


什麼是遞歸? 

程序調用自身的方式叫作遞歸,這裏直接傳送百度百科:遞歸數據結構


遞歸基(Recursion-Base)

遞歸通常會有邊界條件,也稱遞歸基。通常是平凡問題,即能直接求解或給出答案的問題。ide


遞歸思路

遞歸算法的思路就是不斷將複雜問題,或分而治之(Divide-And-Conquer),或減而治之(Decrease-And-Conquer),這是一個從上而下的過程,直到觸碰到遞歸基後,再一步步自下而上,不斷將子問題的答案合併,最終造成最終答案。函數


分而治之(Divide-And-Conquer)

分:即將一個複雜問題根據某個依據,分爲兩個同等規模(不必定徹底相同)的兩個子問題,而後不斷分解下去,直到某個子問題退化爲一個平凡問題(規模爲0或1,或該子問題已有解等狀況)。學習

治:代將平凡問題結果返回,兩個平凡問題答案合併爲一個子問題答案,並不斷返回、合併,最後獲得問題解。ui

應用:如著名的快速排序(Quick-Sort),即將規模爲n的序列排序先分解爲n/2的兩個子問題,再逐自分解,最後再合併。spa


減而治之(Decrease-And-Conquer)

與「分」不一樣,「減」是將當前複雜問題劃分爲:一個平凡問題,一個規模縮減的子問題。子問題會不斷縮減,直到退化爲平凡問題,再逐步合併,獲得答案。code


遞歸算法複雜度分析

遞歸算法的複雜度分析也是一個重點、難點。這裏介紹兩種經常使用方法:

  1. 遞歸跟蹤分析:檢查每一個遞歸實例,累計所需時間,調用語句自己計入對應子實例,其總和即算法執行時間;
  2. 遞歸方程:經過寫出遞推方程,逐步推出複雜度。

這裏須要說明的是:

1,分析遞歸算法複雜度是個難點,須要必定的數學知識,經過不斷練習、分析會逐步提升這方面能力;

2,咱們要學會「封底估算」,這是一大技巧,重點在於咱們要利用數學而不能依賴於數學,緣由是不少時間咱們並不須要獲得一個很精確的數字來代表複雜度是多少,咱們只要能大抵推算出與實際答案同一個數量級的答案便可,好比1000000與1,咱們認爲是相同的,由於都是常量。大多時候咱們甚至能經過直覺、經驗直接得出一個與精確答案相差無幾的答案,而這種估算咱們隨便找張紙都能算出來。


遞歸算法好嗎?

沒有東西在任什麼時候候都是最完美的,只有最合適的。遞歸算法也是這樣。

不得不認可,遞歸算法很重要!不少複雜問題可以經過遞歸算法很簡明的描述出來,並且應該很普遍,樹、圖等都有應用。

但遞歸算法對計算機棧的佔用很大,每次調用一次自身,系統棧就得將自身函數的一些環境壓入,直到算出答案才能彈出,有時候這是不能接受的。尤爲是尾遞歸(在函數尾部返回),如今不少編譯器會自動將尾遞歸改寫。

並且遞歸併無加快運行效率,它縮短了找到問題以前須要走步數,從而減小了運行時間,但這個前提是要合併編寫這個遞歸函數,否則有時候子問題的重複計算會使運行時間更長,好比下面應用中要講到的Fibonacci數。


遞歸應用

Fibonacci數

最大公約數

求進制數

參數:1,要轉換的十進制數;2,保存結果的字符數組;3,要轉換的進制。

返回值:字符數組有效字符個數。

int dectobase(int num, char* conversion, int base){
    if( num > 0 ){
        int cnt = dectobase( num/base, conversion, base) + 1;
        *(conversion + cnt - 1) = num%base + '0';
        return cnt;
    }  
    return 0;
}

 HanoiTower

1 void hanota(int N, char s, char t, char g){
2     if( N > 0 ){
3         hanota( N-1, s, g, t);
4         printf("%c->%c\n", s, g);
5         hanota( N-1, t, s, g);
6     }
7 } 

整數分解爲若干項和

排列組合

尋找中位數

兩個有序序列的中位數

最長公共子序列

八皇后問題 

相關文章
相關標籤/搜索