從遞歸到迭代的方法

在多年的編程中,我已經使用了不少遞歸來解決簡單的問題,可是我徹底意識到有時因爲內存/速度問題須要迭代。 node

所以,在好久之前的某個時候,我去嘗試查找是否存在將通用遞歸方法轉換爲迭代的任何「模式」或教科書方法,卻一無所得。 或至少沒有什麼我能記住的會有所幫助。 編程

  • 有通常規則嗎?
  • 有沒有「模式」?

#1樓

遞歸不過是從另外一個函數調用另外一個函數的過程,只有此過程是經過單獨調用一個函數來完成的。 衆所周知,當一個函數調用另外一個函數時,第一個函數保存其狀態(其變量),而後將控件傳遞給被調用的函數。 能夠經過使用相同的變量名來調用被調用函數,例如fun1(a)能夠調用fun2(a)。 當咱們進行遞歸調用時,沒有新的事情發生。 一個函數經過傳遞相同的類型和類似的名稱變量來調用自身(可是很明顯,存儲在變量中的值是不一樣的,只是名稱保持不變。)。 可是,在每次調用以前,函數都會保存其狀態,而且保存過程將繼續。 保存在堆棧上。 函數

如今,堆棧開始播放了。 spa

所以,若是您編寫一個迭代程序並每次將狀態保存在堆棧上,而後在須要時從堆棧中彈出值,那麼您已成功將遞歸程序轉換爲迭代程序! rest

證實是簡單和分析的。 code

在遞歸中,計算機維護堆棧,而在迭代版本中,您將必須手動維護堆棧。 遞歸

考慮一下,只需將深度優先搜索(在圖形上)遞歸程序轉換爲dfs迭代程序。 內存

祝一切順利! 開發


#2樓

一般,將堆棧溢出用於遞歸函數的技術稱爲蹦牀技術,這是Java開發人員普遍採用的技術。 get

可是,對於C#,這裏有一個小的輔助方法能夠將遞歸函數變成迭代的,而無需更改邏輯或使代碼難以理解。 C#是一種很棒的語言,它可能帶來使人驚奇的東西。

它經過使用輔助方法包裝方法的某些部分來工做。 例如如下遞歸函數:

int Sum(int index, int[] array)
{
 //This is the termination condition
 if (int >= array.Length)
 //This is the returning value when termination condition is true
 return 0;

//This is the recursive call
 var sumofrest = Sum(index+1, array);

//This is the work to do with the current item and the
 //result of recursive call
 return array[index]+sumofrest;
}

變成:

int Sum(int[] ar)
{
 return RecursionHelper<int>.CreateSingular(i => i >= ar.Length, i => 0)
 .RecursiveCall((i, rv) => i + 1)
 .Do((i, rv) => ar[i] + rv)
 .Execute(0);
}

#3樓

努力進行遞歸調用Tail Recursion (遞歸,其中最後一條語句是遞歸調用)。 一旦有了這些,將其轉換爲迭代一般很是容易。


#4樓

在Google上搜索「繼續傳遞樣式」。 有一個轉換爲尾部遞歸樣式的通用過程。 還有將尾遞歸函數轉換爲循環的通用過程。


#5樓

要尋找的一種模式是在函數末尾進行遞歸調用(所謂的尾遞歸)。 能夠輕鬆地將其替換一下子。 例如,函數foo:

void foo(Node* node)
{
    if(node == NULL)
       return;
    // Do something with node...
    foo(node->left);
    foo(node->right);
}

以對foo的調用結束。 能夠替換爲:

void foo(Node* node)
{
    while(node != NULL)
    {
        // Do something with node...
        foo(node->left);
        node = node->right;
     }
}

這樣就消除了第二個遞歸調用。

相關文章
相關標籤/搜索