歡迎閱讀個人我的博客,有更好的排版和文章:https://pushy.sitejavascript
一提及遞歸,我想每一個人都不陌生。舉個從小就聽過的例子:從前有座山,山裏有座廟,廟裏有個和尚,和尚在講故事,從前有座山,山裏有座廟,廟裏有個和尚,和尚在講故事,從前有座山...java
還有你從兩面相對的鏡子中看到的畫面,其實都是抽象出來的遞歸現象,可是嚴格來講並非遞歸,由於會一直重複下去,沒有終止條件,那就稱爲死循環了。有關遞歸和死循環的異同咱們以後會說到,那麼如今先來了解一下什麼是遞歸?node
那麼什麼是遞歸呢? 要理解遞歸,就得先了解什麼是遞歸,實際上這句話就是一個遞歸。這麼說可能很差理解,接下來我舉個簡單的例子來解釋這段話的意義。python
假設咱們如今都不知道什麼是遞歸,咱們天然想到打開瀏覽器:輸入到谷歌的網頁,點擊搜索遞歸,而後在爲維基百科中瞭解到了遞歸的基本定義。在瞭解到了遞歸其實是和棧有關的時候,你又蒙圈了,什麼是棧呢?數據結構沒學清楚,此時的你只能又打開谷歌,搜索什麼是棧。接下來你依次瞭解了內存/操做系統。在你基本瞭解好知識以後,你經過操做系統瞭解了內存,經過內存瞭解了棧,經過棧瞭解了什麼是遞歸這下你恍然大悟!原來這就是遞歸啊!git
這下應該有點明白了吧,這個過程其實就是遞歸的過程,若是不瞭解遞歸,那就先了解什麼是遞歸,可能你會說這是個循環並非遞歸,咱們前面說到,遞歸是須要終止條件的,那麼你明白遞歸是什麼其實就是終止條件。整個過程,搜索引擎充當遞歸函數(只是形象的假設)。在你去依次查找遞歸/棧/內存/操做系統的過程爲前行階段,在你都瞭解完以後,反回去瞭解含義的過程爲退回階段。若是仍是不太清楚,能夠接着看下面的例子。github
也許以前你在網絡上看到過這張圖片:算法
實際上這張圖就很形象地表達出了遞歸,這句嚇得我抱起了抱着抱着抱着個人小鯉魚的個人個人我若是從字面意義上看可能看不出是什麼意思,那麼咱們能夠經過代碼來實現一樣的效果:瀏覽器
function Recursion(depth) { console.log('抱着'); if (!depth) { console.log('個人小鯉魚') } else { Recursion(--depth); // 遞歸調用 } console.log('的我'); } console.log('嚇得我抱起了'); Recursion(2)
在終端的打印結果爲以下,能夠看到和上面的那段話是同樣的:網絡
代碼其實十分簡單,可是須要理解的是:if
代碼塊的條件(!depth
)爲遞歸調用的終止條件,在else
代碼塊內遞歸調用函數.咱們前面有說到遞歸的過程是存在前行和退回階段的,那麼在前行階段咱們在每次調用函數後,打印出了"抱着",而且當depth≠0
時從新調用該函數;在退回階段,將會去執行代碼console.log('的我');
再打印出"的我".數據結構
褚躍躍的圖也能比較清楚地反映出這個過程:
好了!這下你應該明白什麼是遞歸了吧?若是你尚未明白什麼是遞歸的話,你能夠看什麼是遞歸?
有關遞歸應用的應用有不少,例如註明的斐波拉契數列就能夠經過遞歸來實現:
def fib(x): if x < 2: return 0 if x == 0 else 1 # 當x > 2時,開始遞歸調用fib()函數: return fib(x - 1) + fib(x - 2) print(fib(6)) # 打印結果爲:8
這裏須要對i<2
時的特殊狀況作出判斷,當x==0
時,直接返回0
,當x==1
時,直接返回1
首先咱們須要瞭解遍歷的算法:定義的file_display
函數以某個目錄(/home/pushy
)做爲遍歷的起點.遇到一個子目錄時,就先接着遍歷子目錄(遞歸調用函數)。遇到一個文件時,就直接對改文件進行操做(這裏只打印出文件的文件名):
import os def file_display(filepath): for each in os.listdir(filepath): # 獲得文件的絕對路徑: absolute_path = os.path.join(filepath, each) # 獲得是否爲文件仍是目錄的布爾值: is_file = os.path.isfile(absolute_path) if is_file: # 當前的絕對路徑爲文件: print(each) else: # 當前的絕對路徑爲目錄: file_display(absolute_path) file_display('/home/pushy')
這樣咱們就能夠遍歷到/home/pushy
路徑下的全部文件:
另外咱們還可使用遞歸來建立目錄:
import os def createFile(dirname): exits = os.path.exists(dirname) if exits: return True else: # 開始遞歸調用函數,並接受其返回值: rec_result = createFile(os.path.dirname(dirname)) if rec_result: # 若是不存在該目錄,則建立dirname的目錄,並返回已經建立(存在)的值True: os.mkdir(dirname) return True createFile('./aa/bb/cc')
好了,遞歸的知識差很少介紹完了。若是看完上邊大概已經瞭解了循環和遞歸的區別了。對了!簡單來講,循環是有去無回,而遞歸則是有去有回(由於存在終止條件)。
舉個栗子,你用你手中的鑰匙打開一扇門,結果去發現前方還有一扇門,緊接着你又用鑰匙打開了這扇門,而後你又看到一扇們...可是當你開到某扇門時,發現前方是一堵牆無路可走了,你選擇原路返回——這就是遞歸
可是若是你打開一扇門後,一樣發現前方也有一扇們,緊接着你又打開下一扇門...直到打開最後一扇門出去,或者一直沒有碰到盡頭 (死循環)——這就是循環。
參考資料: