對於遞歸的傲慢與偏見

最近刷leetcode 79題 Word Search須要用到DFS算法,因爲是刷leetcode,心想不能用遞歸,影響效率,因而利用stack完成。以後又利用遞歸(使用cache)實現了一次,結果居然是遞歸的算法比非遞歸更快算法

「低效」的遞歸

對於遞歸,一般會有效率低下的映像,通常是由於2點:編程

  • 重複計算
  • 函數調用開銷

對於重複計算,能夠緩存計算結果來解決。數組

對於函數調用開銷,能夠利用「尾遞歸」來解決,不過目前的v8引擎並無實現對尾遞歸的優化,因此最開始我覺得遞歸沒有理由比非遞歸更快。緩存

遞歸與堆棧

非遞歸的DFS算法使用一個「堆棧」來實現。而一樣,函數調用也是利用「棧」來完成。數據結構

首先,Javascipt並無原生的堆棧這個數據結構,一般是利用數組來實現,效率上應該會有所損失。函數式編程

其次,系統堆棧快於手動堆棧是沒有疑問的,並且系統堆棧使用的是寄存器,比內存要快不少。函數

最後,函數調用會有額外開銷這個是無法避免的,可是當函數變量很少,遞歸層級不深的時候,這個開銷帶來的效率損失不能抵消系統堆棧帶來的效率提高。優化

綜合來看,在不爆棧的狀況下,大部分Javascript代碼裏使用了緩存的遞歸在算法效率上高於非遞歸算法,而且遞歸算法的表現力是徹底高於非遞歸的。不少時候,出於臆斷進行的所謂優化,徹底是負優化code

關於遞歸的隨想

以前在看SICP的時候,發現函數式編程沒有循環,非函數式語言的循環操做都是利用「遞歸」的形式來完成的。並且全部的遞歸,均可以改爲迭代的形式,避免了遞歸重複計算的缺點,也無需使用緩存來加速遞歸的計算,省下了緩存的開銷,因此有句話叫作「全部循環都是尾遞歸」。遞歸

總結

  • 慣性思惟不可取,實踐檢驗真理
  • 遞歸 !== 慢
  • 之後圖的遍歷、樹的遍歷、巴拉巴拉其它狀況,直接寫遞歸,誰懟我說遞歸效率低,就讓他來solo。(莫名的開心咋回事兒啊?)
  • 以上關於爲何遞歸快的推理全是推斷,可是DFS非遞歸慢於遞歸是事實(Javascript中), 跪求大神給出準確解讀。
相關文章
相關標籤/搜索