【轉】循環、迭代、遍歷和遞歸

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

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

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

迭代

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

利用迭代算法解決問題,須要作好如下三個方面的工做:數組

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

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

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

迭代與循環

先從字面上看:優化

  • 迭代:「迭」:輪流,輪番,替換,交替,更換。「代」:代替。因此迭代的意思是:變化的循環,這種變化就是輪番代替,輪流代替。
  • 循環:不變的重複。

我的認爲迭代是循環的一種,循環體代碼分爲固定循環體,和變化的循環體。spa

固定循環舉例:.net

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

實現迭代翻譯

$sum = 0;

for($i = 1; $i <= 1000; $i++ ){
    $sum = $sum + i;
}
 

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

迭代的好處:迭代減小了冗餘代碼,提升了代碼的利用率和動態性。

循環、迭代與遞歸

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

固然,從理論上說,全部的遞歸函數均可以轉換爲迭代函數,反之亦然,然而代價一般都是比較高的。但從算法結構來講,遞歸聲明的結構並不總可以轉換爲迭代結構,緣由在於結構的引伸自己屬於遞歸的概念,用迭代的方法在設計初期根本沒法實現,這就像動多態的東西並不老是能夠用靜多態的方法實現同樣。這也是爲何在結構設計時,一般採用遞歸的方式而不是採用迭代的方式的緣由,一個極典型的例子相似於鏈表,使用遞歸定義及其簡單,但對於內存定義(數組方式)其定義及調用處理說明就變得很晦澀,尤爲是在遇到環鏈、圖、網格等問題時,使用迭代方式從描述到實現上都變得很不現實。

2. 遞歸實際上是方便了程序員難爲了機器。它只要獲得數學公式就能很方便的寫出程序。優勢就是易理解,容易編程。但遞歸是用棧機制實現的,每深刻一層,都要佔去一塊棧數據區域,對嵌套層數深的一些算法,遞歸會力不從心,空間上會之內存崩潰而了結,並且遞歸也帶來了大量的函數調用,這也有許多額外的時間開銷。因此在深度大時,它的時空性就很差了。

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

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

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

原理上講,全部遞歸都是能夠消除的,代價就是可能本身要維護一個棧。並且我我的認爲,不少狀況下用遞歸仍是必要的,它每每能把複雜問題分解成更爲簡單的步驟,並且很能反映問題的本質。

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

轉自:http://www.nowamagic.net/librarys/veda/detail/2324

相關文章
相關標籤/搜索