1. 程式人生 > >數據結構第九篇——棧與遞歸

數據結構第九篇——棧與遞歸

分解 是什麽 運行時 使用 執行過程 非遞歸算法 long 應該 char

棧還有一個重要應用是在程序設計中實現遞歸。遞歸是計算機 科學和數學中一種解決問題的及其重要的方法。在數據結構中,可以用它來設計簡單。易於理解的算法,特別是在一些具有遞歸定義的結構上設計算法。

遞歸的概念

一個直接或間接地調用自己的函數,稱作遞歸函數。遞歸是程序設計中一個強有力的方法。

遞歸函數和運行時棧

棧還有一個重要應用就是在程序設計語言中實現函數調用。當一個函數在運行期間調用另一個函數時,在運行被調用函數之前,系統需要將實際參數和返回值地址等數據傳遞給被調函數,當函數調用時,這些數據與局部變量一起構成一條“工作記錄”,被壓入系統提供的棧。

下面用一個簡單的階乘函數的執行過程來說明遞歸與棧的關系。描述如下:

1 int fact(int w)
2 {
3     if(w==0)
4     return 1;
5     else
6     t=fact(w-1)
7     return (w*t);
8 } 

遞歸算法的設計方法

遞歸時解決復雜問題的一種有效方法。如果掌握了遞歸算法的設計思路,就能比較容易地解決一類復雜的問題。但是什麽樣的問題可以用遞歸來解決呢?如何設計遞歸算法呢?

一般來說,如果一個復雜的問題能夠被分解成若幹個同類型的子問題,那麽這個問題可以用遞歸算法實現。

在設計遞歸算法時,應該遵循下面的規則:

①定義一個最小規模的問題,並給出其解。

②把復雜的問題劃分為同類型的若幹規模較小的子問題 ,並分別解決子問題。

③把各子問題旳解組合起來,即可得到原問題的解。

例如:漢諾塔問題:

1 void Hanoi(int n,char A,char B,char C)
2 {
3     if(n>0)                //n=0是最小子問題,不做任何動作,直接退出 
4     {
5         Hanoi(n-1,A,C,B);    //子問題①:將A上面的n-1個盤子移到B上 
6         Move(A,n.C);        //將編號為n的圓盤從A移到C 
7         Hanoi(n-1,B,A,C);    //子問題②:將B上的n-1個盤子移到C上 
8     }
9 }

遞歸算法的分析
利用遞歸使我們設計算法和編程都非常簡單,可讀性高。但是往往導致算法效率比較低。

這體現了算法的簡單性與效率之間的矛盾。

用Fibonacci數列來說明這個問題。

1 long Fib(int n)
2 {
3     if(n==1||n==2)
4     return 1;
5     else
6     return Fib(n-1)+Fib(n-2);
7 } 

從調用情況來看,相同實參的同一個函數被調用了多次。求Fib(6)時,Fib(3)被調用了3次,Fib(2)被調用了5次,總共調用的次數為15次。算法時間復雜度為O(2n)。
為了便於比較,下面給出非遞歸算法解決Fibonacci問題。

 1 long FibIter(int n)
 2 {
 3     long oneback,twoback,current;
 4     oneback=1;
 5     twoback=1;
 6     
 7     if(n==1||n==2)
 8     return 1;
 9     else
10     {
11         for(int i=0;i<n;i++)
12         {
13             current=oneback+twoback;
14             twoback=oneback;
15             oneback=current;
16         }
17     }
18     return current;
19 }

很顯然,以上算法當n>=3時,時間復雜度是n-2,遠小於遞歸算法的時間復雜度。

需要註意的是,在使用遞歸時必須權衡設計簡單與算法的時間和空間復雜度的關系,再有足夠並且效率要求不高的情況下可以使用遞歸。

數據結構第九篇——棧與遞歸