循環迭代遍歷遞歸的區別


  loop、iterate、traversal和recursion這幾個詞是計算機技術書中經常會出現的幾個詞匯。眾所周知,這幾個詞分別翻譯為:循環、迭代、遍歷和遞歸。乍一看,這幾個詞好像都與重復(repeat)有關,但有的又好像不完全是重復的意思。那么這幾個詞到底各是什么含義,有什么區別和聯系呢?下面就試着解釋一下。

  • 循環(loop),指的是在滿足條件的情況下,重復執行同一段代碼。比如,while語句。
  • 迭代(iterate),指的是按照某種順序逐個訪問列表中的每一項。比如,for語句。
  • 遍歷(traversal),指的是按照一定的規則訪問樹形結構中的每個節點,而且每個節點都只訪問一次。
  • 遞歸(recursion),指的是一個函數不斷調用自身的行為。比如,以編程方式輸出著名的斐波納契數列。

有了以上定義,這幾個概念之間的區別其實就比較清楚了。至於它們之間的聯系,嚴格來講,它們似乎都屬於算法的范疇。換句話說,它們只不過是解決問題的不同手段和方式,而本質上則都是計算機編程中達成特定目標的途徑。

迭代

迭代算法是用計算機解決問題的一種基本方法。它利用計算機運算速度快、適合做重復性操作的特點,讓計算機對一組指令(或一定步驟)進行重復執行,在每次執行這組指令(或這些步驟)時,都從變量的原值推出它的一個新值。

利用迭代算法解決問題,需要做好以下三個方面的工作:

  1. 確定迭代變量。在可以用迭代算法解決的問題中,至少存在一個直接或間接地不斷由舊值遞推出新值的變量,這個變量就是迭代變量。
  2. 建立迭代關系式。所謂迭代關系式,指如何從變量的前一個值推出其下一個值的公式(或關系)。迭代關系式的建立是解決迭代問題的關鍵,通常可以使用遞推或倒推的方法來完成。
  3. 對迭代過程進行控制。在什么時候結束迭代過程?這是編寫迭代程序必須考慮的問題。不能讓迭代過程無休止地重復執行下去。迭代過程的控制通常可分為兩種情況:一種是所需的迭代次數是個確定的值,可以計算出來;另一種是所需的迭代次數無法確定。對於前一種情況,可以構建一個固定次數的循環來實現對迭代過程的控制;對於后一種情況,需要進一步分析出用來結束迭代過程的條件。

可以用迭代的算法有很經典的問題,比如兔子產子問題:假定你有一雄一雌一對剛出生的兔子,它們在長到一個月大小時開始交配,在第二月結束時,雌兔子產下另一對兔子,過了一個月后它們也開始繁殖,如此這般持續下去。每只雌兔在開始繁殖時每月都產下一對兔子,假定沒有兔子死亡,在一年后總共會有多少對兔子?

還有上樓梯的走法問題:有一段樓梯有10級台階,規定每一步只能跨一級或兩級,要登上第10級台階有幾種不同的走法?

這兩個問題可以參看以前寫的一篇文章:趣味算法之兔子產子問題

迭代與循環

先從字面上看:

  • 迭代:“迭”:輪流,輪番,替換,交替,更換。“代”:代替。所以迭代的意思是:變化的循環,這種變化就是輪番代替,輪流代替。
  • 循環:不變的重復。

個人認為迭代是循環的一種,循環體代碼分為固定循環體,和變化的循環體。

固定的循環舉例:

1 for($i=0; $i < 8; $i++){
2     echo 'Welcome to NowaMagic';
3 }

實現迭代:

1 $sum = 0;
2  
3 for($i = 1; $i <= 1000; $i++ ){
4     $sum $sum + i;
5 }

上面的迭代是常見的遞增式迭代。類似的還有遞減式迭代,遞乘式迭代。

迭代的好處:迭代減少了冗余代碼,提高了代碼的利用率和動態性。

循環、迭代與遞歸

1. 遞歸算法與迭代算法的設計思路區別在於:函數或算法是否具備收斂性,當且僅當一個算法存在預期的收斂效果時,采用遞歸算法才是可行的,否則,就不能使用遞歸算法。

當然,從理論上說,所有的遞歸函數都可以轉換為迭代函數,反之亦然,然而代價通常都是比較高的。但從算法結構來說,遞歸聲明的結構並不總能夠轉換為迭代結構,原因在於結構的引申本身屬於遞歸的概念,用迭代的方法在設計初期根本無法實現,這就像動多態的東西並不總是可以用靜多態的方法實現一樣。這也是為什么在結構設計時,通常采用遞歸的方式而不是采用迭代的方式的原因,一個極典型的例子類似於鏈表,使用遞歸定義及其簡單,但對於內存定義(數組方式)其定義及調用處理說明就變得很晦澀,尤其是在遇到環鏈、圖、網格等問題時,使用迭代方式從描述到實現上都變得很不現實。

2. 遞歸其實是方便了程序員難為了機器。它只要得到數學公式就能很方便的寫出程序。優點就是易理解,容易編程。但遞歸是用棧機制實現的,每深入一層,都要占去一塊棧數據區域,對嵌套層數深的一些算法,遞歸會力不從心,空間上會以內存崩潰而告終,而且遞歸也帶來了大量的函數調用,這也有許多額外的時間開銷。所以在深度大時,它的時空性就不好了。

循環其缺點就是不容易理解,編寫復雜問題時困難。優點是效率高。運行時間只因循環次數增加而增加,沒什么額外開銷。空間上沒有什么增加。

3. 局部變量占用的內存是一次性的,也就是O(1)的空間復雜度,而對於遞歸(不考慮尾遞歸優化的情況),每次函數調用都要壓棧,那么空間復雜度是O(n),和遞歸次數呈線性關系。

4. 遞歸程序改用循環實現的話,一般都是要自己維護一個棧的,以便狀態的回溯。如果某個遞歸程序改用循環的時候根本就不需要維護棧,那其實這個遞歸程序這樣寫只是意義明顯一些,不一定要寫成遞歸形式。但很多遞歸程序就是為了利用函數自身在系統棧上的auto變量記錄狀態,以便回溯。

原理上講,所有遞歸都是可以消除的,代價就是可能自己要維護一個棧。而且我個人認為,很多情況下用遞歸還是必要的,它往往能把復雜問題分解成更為簡單的步驟,而且很能反映問題的本質。

遞歸其實就是利用系統堆棧,實現函數自身調用,或者是相互調用的過程。在通往邊界的過程中,都會把單步地址保存下來,知道等出邊界,再按照先進后出的進行運算,這正如我們裝木桶一樣,每一次都只能把東西方在最上面,而取得時候,先放進取的反而最后取出。遞歸的數據傳送也類似。但是遞歸不能無限的進行下去,必須在一定條件下停止自身調用,因此它的邊界值應是明確的。就向我們裝木桶一樣,我們不能總是無限制的往里裝,必須在一定的時候把東西取出來。比較簡單的遞歸過程是階乘函數,你可以去看一下。但是遞歸的運算方法,往往決定了它的效率很低,因為數據要不斷的進棧出棧。


免責聲明!

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



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