關於遞歸算法的時間和空間復雜度


斐波那契序列:在下面的代碼中,可以看到函數 fibonacci (int n) 計算了第 n 個斐波那契序列。斐波那契數列是 0, 1, 1, 2, 3, 5, 8, 13, 21,...。如你所見,該序列的第0個數為 0,該序列的第1個數為 1,依此類推。通常,如果 f(n) 表示斐波那契數列的第 n 個數字,則  f(n) = f(n-1) + f(n-2) 。對於此遞歸關系,f(0) = 0 和 f(1) = 1是終止條件。

1 public int fibonacci(int n) {
2     if (n == 0 || n == 1) {
3         return n;
4     }  
5 
6     return (fibonacci(n-1) + fibonacci(n-2));
7 }

時間復雜度:讓我們看一下生成的遞歸樹以計算斐波那契數列的第 5 個數。

 

  在此遞歸樹中,每個狀態(除 f(0) 和 f(1) 之外)都會生成兩個附加狀態,並且生成的狀態總數為 15。通常,用於計算第 n 個斐波那契數 f (n) 的狀態總數大約等於 2^n。注意,每個狀態都表示對 fibonacci(n) 的函數調用,該函數除了進行另一個遞歸調用外不執行任何操作。因此,計算斐波那契數列第 n 個所需的總時間為 O (2^n)。
  請注意,這並不總是正確,為了更准確地進行時間復雜度分析,應該使用主定理。該解釋的目的是對遞歸算法的運行時間有一個總體了解。  

空間復雜度:遞歸算法的空間復雜度計算有些棘手。我們需要了解如何在內存中生成堆棧幀以進行遞歸調用序列。

  讓我們嘗試大致了解何時生成堆棧幀以及它們在內存中保留了多長時間?從方法 "f0" 調用方法 "f1" 時,將創建與該方法 "f1" 相對應的堆棧幀。該堆棧幀將保留在內存中,直到函數 "f1" 的調用未終止。該堆棧幀負責保存函數“f1”的參數,函數 "f1" 中的局部變量以及調用方函數(函數 "f0")的返回地址。現在,當此函數 "f1" 調用另一個函數 "f2" 時,也會生成與 "f2" 相對應的堆棧幀,並將其保留在內存中,直到對 "f2" 的調用未終止。調用 "f2" 時,其中具有堆棧框架的調用堆棧如下所示:

 

  現在,當返回對函數 "f2" 的調用時,由於不再需要與 "f2" 相對應的堆棧幀,因此將從內存中刪除該堆棧幀。函數 "f1" 和函數 "f0" 的堆棧幀也是如此。

  使用此類推方法進行遞歸調用序列時,應遵循的原則是,在任何時間點內存中可能存在的最大堆棧幀數等於遞歸樹的最大深度在遞歸樹中,當執行與葉節點狀態相對應的調用時,其調用序列可以由從遞歸樹中的根節點到該葉節點的路徑表示。例如,在用於計算第五斐波那契數的遞歸樹中,當執行最左端和最底端的狀態 f(1) 時,導致該狀態的調用序列為 f(5) -> f(4) -> f (3) -> f(2) -> f(1),所有對應的堆棧幀將出現在內存中,並且當 f(1) 返回時,其堆棧幀將從內存(或調用堆棧)中刪除

  總而言之,遞歸算法的空間復雜度與所生成的最大遞歸樹的深度成正比。如果遞歸算法的每個函數調用都占用 O(m) 空間,並且如果遞歸樹的最大深度為 n,則遞歸算法的空間復雜度將為 O(n·m)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM