一、遞歸
1.遞歸的定義
遞歸是函數嵌套調用中的一種特殊形式,函數在定義時,間接或直接調用自身,叫遞歸調用。
#直接調用本身
def f1():
print('============>遞歸')
f1()
func()
# #間接調用本身
def f1():
print('=============>我是f1')
f2()
def f2():
print('=============>我是f2')
f1()
f2()
# 直接調用或者間接調用本身,形成死循環,這種遞歸是無意義的遞歸,超過python限制的調用層級,代碼會報錯。
RecursionError: maximum recursion depth exceeded while calling a Python object
調用函數會產生局部的名稱空間,佔用內存,因爲上述這種調用會無需調用本身,python解釋器的內存管理機制爲了防止其無限制佔用內存,對函數的遞歸調用做了最大的層級限制。
2. 修改遞歸調用最大深度
import sys
sys.getrecursionlimit()
sys.setrecursionlimit(2000) #將默認的1000修改爲2000
def f1(n):
print('from f1',n)
f1(n+1)
f1(1)
雖然可以設置 遞歸(循環)的最大深度,但是每遞歸一次,對應的函數名和值都會儲存在內存中,而且無限制地遞歸調用本身是毫無意義的,遞歸應該分爲兩個明確的階段,回溯與遞推。
3.遞歸的特點
- 特點1:遞推
- 特點2:回溯
由上圖可知:
回溯:就是從外向內一層一層遞歸調用下去,回溯階段必須要有一個明確的結束條件,每進入下一次遞歸,問題的規模都應該有所減少。
遞推:就是獲取那個最總結束條件,並一層層的結束遞歸。
#遞歸小練習,取出下方列表的值
lists = [1, [3, [5, [89, [67, [23,]]]]]]
def func(lists):
for item in lists:
if type(item) is list:
func(item)
else:
print(item)
res = func(lists)
print(res)
4.遞歸總結
-
必須有一個明確的結束條件
-
每進入深一層級遞歸時,問題規模要比上次有所減少(單純調用自身是無意義的)
-
遞歸效率不高,遞歸層級過多會導致棧溢出。
在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出
二、二分法
想從一個按照從小到大排列的數字列表中找到指定的數字,遍歷的效率太低,用二分法(算法的一種,算法是解決問題的方法)可以極大低縮小問題規模
l=[1,2,10,30,33,99,101,200,301,311,402,403,500,900,1000] #從小到大排列的數字列表
def search(n,l):
print(l)
if len(l) == 0:
print('not exists')
return
mid_index=len(l) // 2
if n > l[mid_index]:
#in the right
l=l[mid_index+1:]
search(n,l)
elif n < l[mid_index]:
#in the left
l=l[:mid_index]
search(n,l)
else:
print('find it')
search(3,l)
## 實現類似於in的效果
對於一個有序的序列可以使用二分法提高查找效率
原理: 將整個序列分爲兩半然後判斷要找的數據是在左邊還是右邊從而排除一半的數據