日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網營銷解決方案
深入理解遞歸,是你誤解了遞歸

遞歸是一個神奇的算法,它是編程書籍中講解的最尷尬部分。這些書籍通常會展示一個遞歸的階乘實現(xiàn),然后警告你,雖然它能運行但是它非常的慢并且可能會堆棧溢出而崩潰。雖然大家對它持懷疑態(tài)度,但是這不影響遞歸是算法中最強大的想法。

成都創(chuàng)新互聯(lián)制作網站網頁找三站合一網站制作公司,專注于網頁設計,成都做網站、網站制作,網站設計,企業(yè)網站搭建,網站開發(fā),建網站業(yè)務,680元做網站,已為數(shù)千家服務,成都創(chuàng)新互聯(lián)網站建設將一如既往的為我們的客戶提供最優(yōu)質的網站建設、網絡營銷推廣服務!

讓我們來看看經典的遞歸階乘:

factorial.c

 
 
 
 
  1. #include 
  2. int factorial(int n)
  3. {
  4.         int previous = 0xdeadbeef;
  5.         if (n == 0 || n == 1) {
  6.                 return 1;
  7.         }
  8.         previous = factorial(n-1);
  9.         return n * previous;
  10. }
  11. int main(int argc)
  12. {
  13.         int answer = factorial(5);
  14.         printf("%d\n", answer);
  15. }
  16.     

一個函數(shù)調用自身的想法起初非常神秘。為了解釋整個過程,下圖展示了factorial(5)被調用到n == 1 棧上結構。

每次調用factorial都會生成一個新的棧幀。這些棧幀的創(chuàng)建和銷毀使得遞歸因子比其迭代部分慢。在調用開始和返回之前的這些棧幀累積是可能耗盡棧空間并使程序崩潰。

但是這些擔憂通常是理論上的。例如,棧幀 factorial每個占用16個字節(jié)(這可以根據棧對齊和其他因素而變化)。如果您在計算機上運行現(xiàn)代x86 Linux內核,通常默認有8兆字節(jié)的堆??臻g,因此factorial n最多可以處理512,000。這是一個巨大數(shù),需要8,971,833位來表示這個數(shù),所以??臻g是我們問題中最少的:一個微弱的整數(shù) - 甚至是64位 - 在我們用完??臻g之前會溢出數(shù)萬次。

我們稍后會看一下CPU的使用情況,但是現(xiàn)在讓我們從位和字節(jié)中退一步,看看遞歸作為一種通用技術。我們的階乘算法歸結為將整數(shù)N,N-1,... 1推入堆棧,然后以相反的順序將它們相乘。我們使用程序的調用堆棧執(zhí)行此操作的前提是:我們可以在堆上分配堆棧并使用它。雖然調用堆棧確實具有特殊屬性,但它只是您可以使用的另一種數(shù)據結構。

一旦你看到調用堆棧作為一個數(shù)據結構,其他東西就變得豁然開朗了:將本身之前所有這些整數(shù)累加起來再乘以自身這顯然不是明智的選擇。 使用迭代過程計算階乘更為明智。

有一個傳統(tǒng)的面試問題,在迷宮中放一只老鼠,你幫助老鼠找奶酪,假設老鼠可以在迷宮中向左或向右轉。你會如何建模并解決這個問題?

像生活中的大多數(shù)問題一樣,你可以將這種嚙齒動物的任務抽象到一個圖形,特別是一個二叉樹,其中節(jié)點代表迷宮中的位置。然后你可以盡可能地讓老鼠左轉,當它到達死胡同時回溯然后右轉。下圖就是老鼠路徑 :

每條邊(線)都可以左轉或右轉,老鼠可以選擇。如果任一轉彎被阻止,則相應的邊緣不存在。無論您使用調用堆棧還是其他數(shù)據結構,此過程本質上都是遞歸的。但使用調用棧非常簡單:

Maze.c

 
 
 
 
  1. #include 
  2. #include "maze.h"
  3. int explore(maze_t *node)
  4. {
  5.   int found = 0;
  6.     if (node == NULL) {
  7.         return 0;
  8.     }
  9.     if (node->hasCheese) {
  10.         return 1; // found cheese
  11.     }
  12.   found = explore(node->left) || explore(node->right);
  13.   return found;
  14. }
  15. int main(int argc)
  16. {
  17.         int found = explore(&maze);
  18. }

在maze.c:13中找到奶酪,下圖是堆棧。

雖然這里很難擺脫遞歸,但這并不意味著它必須通過調用棧來完成。例如,你可以使用一個字符串 RRLL來跟蹤轉彎,并依靠字符串來決定鼠標的下一步行動。或者你可以分配其他變量來記錄奶酪尋找的狀態(tài)。你仍然在實現(xiàn)遞歸過程,但滾動你自己的數(shù)據結構。

這可能會更復雜,因為調用堆棧就像手套一樣。每個堆棧幀不僅記錄當前節(jié)點,還記錄該節(jié)點中的計算狀態(tài)(在這種情況下,我們是僅采用左側還是已經嘗試右側)。然而,我們有時會因為害怕溢出而放棄了美好的東西。在我看來是非常愚蠢的。

正如我們所看到的,棧很大,并且在??臻g之前經常會遇到其他約束。還可以檢查問題的大小并確??梢园踩靥幚怼PU擔心主要是由兩個廣泛的病理學例子灌輸:愚蠢的因子和可靠的O(2 n) 遞歸Fibonacci沒有記憶。這些并不表示理智的堆棧遞歸算法。

現(xiàn)實情況是棧操作很快。數(shù)據的偏移是準確的,棧在緩存中,不需要冷啟動,并且有專門的指令來完成工作。同時,使用您自己的堆分配數(shù)據結構會產生大量開銷。會看到其他人編寫的東西比調用堆棧遞歸更復雜,性能更差。

現(xiàn)代CPU 非常優(yōu)秀了,通常不是瓶頸。簡單往往和性能等同。


文章名稱:深入理解遞歸,是你誤解了遞歸
瀏覽地址:http://m.5511xx.com/article/dhdijoi.html