看過我前面部落格的朋友都清楚,函式呼叫主要依靠ebp和esp的堆疊互動來實現的。那麼遞迴呢,最主要的特色就是函式自己呼叫自己。如果乙個函式呼叫的是自己本身,那麼這個函式就是遞迴函式。
我們可以看一下普通函式的呼叫怎麼樣的。試想如果函式a呼叫了函式b,函式b又呼叫了函式c,那麼在堆疊中的資料是怎麼儲存的呢?
int iterate(int value)
下面我們使用乙個函式進行呼叫,看看會發生什麼情況?
void process()
看看此時記憶體堆疊是什麼樣的?
iterate(int 1) line 96
iterate(int 2) line 97 + 12 bytes
iterate(int 3) line 97 + 12 bytes
iterate(int 4) line 97 + 12 bytes
iterate(int 5) line 97 + 12 bytes
iterate(int 6) line 97 + 12 bytes
process() line 102 + 7 bytes
main() line 108
maincrtstartup() line 206 + 25 bytes
kernel32! 7c817067()
大家也看到了上面的**,遞迴函式和普通的函式也沒有什麼差別。除了自己呼叫本身之外,他就是乙個普通的函式。那麼這個函式遞迴到什麼時候返回呢?這就是遞迴函式的關鍵了。我們看到iterate函式到1就停止了,所以上面的堆疊在(value == 1)即return。所以乙個遞迴函式最關鍵的部分就是兩點:(1)遞迴策略;(2)函式出口。
看到這裡,大家可能感到遞迴函式不過如此,事實上也是這樣。但是,還有一點大家需要牢記在心,遞迴的深度是我們必須考慮的乙個問題。只有遞迴深度在乙個可控的範圍內,那麼整個遞迴過程都是可控的。那什麼時候不可控呢?那就是遞迴深度超過了一定的數字?這個數字和具體的執行緒堆疊長度有關?等到堆疊溢位了,那麼獲得的資料已經失去了真實性,所以也就沒有意義了。
我們把上面的問題推廣一下,如何用自己定義的堆疊模擬上面的遞迴呼叫呢?這樣既能滿足遞迴的屬性,又能確保函式深度可控。
大家可以先寫一下自己的方案,下面只是我個人的乙個思路。
int iterate(int value)
return count;
}
一步一步寫演算法(之遞迴和堆疊)
看過我前面部落格的朋友都清楚,函式呼叫主要依靠ebp和esp的堆疊互動來實現的。那麼遞迴呢,最主要的特色就是函式自己呼叫自己。如果乙個函式呼叫的是自己本身,那麼這個函式就是遞迴函式。我們可以看一下普通函式的呼叫怎麼樣的。試想如果函式a呼叫了函式b,函式b又呼叫了函式c,那麼在堆疊中的資料是怎麼儲存的...
一步一步寫演算法(之函式堆疊顯示)
在繼續圖的討論之前,我們今天開個小差,討論一下函式堆疊的基本原理。有過程式設計經驗的朋友都知道,堆疊除錯是我們在程式開發中經常應用的乙個功能。那麼大家有沒有想過,函式堆疊是怎麼開始的啊?其實我們可以自己寫乙個函式堆疊輸出函式分析一下。因為一般來說,函式的壓棧過程是這樣的 引數三 引數二 引數一 ad...
一步一步寫演算法(之 A 演算法)
在前面的部落格當中,其實我們已經討論過尋路的演算法。不過,當時的示例圖中,可選的路徑是唯一的。我們挑選乙個演算法,就是說要把這個唯一的路徑選出來,怎麼選呢?當時我們就是採用窮盡遞迴的演算法。然而,今天的情形有點不太一樣了。在什麼地方呢?那就是今天的路徑有n條,這條路徑都可以達到目的地,然而我們在挑選...