本篇將介紹遞迴與尾遞迴的相關內容。
一、什麼是“遞迴”?
遞迴是一種優雅的解決問題的方法。
看一段最簡單的遞迴例子:
Fibonacci
數(斐波那契數):我們都知道Fibonacci
數的遞推公式為:
- F(0)=F(1)=1,
- 當n>=2時,F(n)=F(n-1)+F(n-2)
用Python寫,就是這樣:
def Fibonacci(n):
if(n>=2):
return Fibonacci(n - 1) + Fibonacci(n - 2)
elif (n==0 or n==1):
return 1
else:
return -1
print Fibonacci(20)
複製程式碼
遞迴,簡單來說,就是在執行的過程中呼叫自己。
遞迴能幫我們處理一些複雜的演算法問題,但絕不能濫用遞迴。 在程式設計角度,迴圈的效能要好於遞迴。 從開發角度,使用遞迴,邏輯上更容易被理解。 所以,要分場合使用遞迴,用好遞迴。
二、基線條件和遞迴條件
一個遞迴的實現一定少不了基線條件和遞迴條件。
那麼,什麼是“基線條件”?什麼又是“遞迴條件”呢?
名稱 | 描述 |
---|---|
遞迴條件 | 函式呼叫自己的條件。 |
基線條件 | 函式不再呼叫自己的條件,從而避免形成無限迴圈。 |
拿上面Fibonacci
的例子來說,
def Fibonacci(n):
if(n>=2):
return Fibonacci(n - 1) + Fibonacci(n - 2)
elif (n==0 or n==1):
return 1
else:
return -1
複製程式碼
- 遞迴條件:就是
if(n>=2)
。 - 基線條件:就是
elif (n==0 or n==1)
。
PS:在python中,
else if
的語法是elif
。
三、棧
本節涉及到了記憶體方面的知識——呼叫棧(call stack
)。
棧是一種簡單的資料結構,當我們呼叫方法時,系統會執行“壓棧”操作;當我們呼叫完方法時,系統會執行“出棧”操作。
簡單來說,
- 函式呼叫 就意味著 => 申請棧幀,函式入棧。
- 函式返回 就意味著 => 推出棧幀,函式出棧。
PS:不過還有一種特殊的情況:叫做尾呼叫優化(其本質是複用棧幀,即函式呼叫時,不再申請新棧幀,而是複用舊的棧幀。),在下文
3.3
節會重點講解。
3.1 呼叫棧
我們來看這樣一段程式碼:
def func1(param1):
func2(param1)
func3(param1)
def func2(param2):
print param2
def func3(param3):
print param3
func1(647)
複製程式碼
解析:定義了三個函式,分別是func1
、func2
、func3
。其中傳入的引數名為param1
、param2
、param3
。
而在記憶體中,會做如下操作:
3.2 遞迴呼叫棧
遞迴函式也會使用呼叫棧,我們稱之為“遞迴呼叫棧”。
下面,請看這個例子:
def factorial(x):
if x == 1:
return 1
else:
return x * factorial(x-1)
print factorial(3)
複製程式碼
解析:這是一個求階乘的遞迴函式。傳入引數x,得出x*x-1...*1的值(x>=1)。 而每一次遞迴,都會申請一個棧幀,這種棧幀就叫做遞迴呼叫棧。
圖解如下:
3.3 尾遞迴
尾遞迴是一種高階遞迴方式,它可以不斷的複用舊棧幀,已達到最大的記憶體優化。
注意:不是所有語言都支援尾遞迴優化(尾呼叫優化)。
JavaScript、Objective-C、Java、C++等支援尾遞迴優化,而Python本身是不支援尾遞迴優化的。
(關於iOS中OC的尾呼叫優化可以看這篇:iOS objc_msgSend尾呼叫優化機制詳解)
Q1:什麼是尾遞迴?什麼又是尾呼叫?
尾遞迴:在函式最後一步,僅僅返回撥用了自身。(注意僅僅兩字) 尾呼叫:在函式最後一步,僅僅返回了一個函式。(注意僅僅兩字) 所以,尾遞迴實際上是屬於尾呼叫的一種特殊情形。
Q2:舉個尾遞迴的例子?
int fun(int x) {
if (x > 0)
return fun(x-1);
else
return 1;
}
複製程式碼
在函式的最後一步,僅僅return了本身的函式。符合尾遞迴。
Q3:尾遞迴究竟做了什麼優化?
兩張對比圖一目瞭然:
- 非尾呼叫:
- 是尾呼叫:
Q4:尾遞迴的本質是什麼?
答:棧幀的重複利用。
小編微信:可加並拉入《QiShare技術交流群》。
關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)
推薦文章:
iOS 避免常見崩潰(二)
演算法小專欄:選擇排序
iOS Runloop(一)
iOS 常用除錯方法:LLDB命令
iOS 常用除錯方法:斷點
iOS 常用除錯方法:靜態分析
iOS 訊息轉發
奇舞週刊