Python入門之遞歸函數


遞歸函數

在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。

遞歸函數特性:

  1. 必須有一個明確的結束條件;
  2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
  3. 相鄰兩次重復之間有緊密的聯系,前一次要為后一次做准備(通常前一次的輸出就作為后一次的輸入)。
  4. 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出)

先舉個簡單的例子:計算1到100之間相加之和;通過循環和遞歸兩種方式實現

#!/usr/bin/env python3
# 1-100 sum

import sys
def sum_cycle(n):
    '''
    1 to n,The sum function
    '''
    sum = 0
    for i in range(1,n + 1):
        sum += i
    return sum

def sum_recu(n):
    '''
    1 to n,The sum function
    '''
    if n > 0:
        return n + sum_recu(n - 1)    #調用函數自身
    else:
        return 0
print("循環求和:",sum_cycle(100))
print("遞歸求和:",sum_recu(100))
執行結果:
[root@localhost tmp]# ./sum.py
循環求和: 5050
遞歸求和: 5050

遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。

***使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。

把上面的遞歸求和函數的參數改成10000就導致棧溢出!

[root@localhost tmp]# ./sum.py
循環求和: 5050
Traceback (most recent call last):
  File "./sum.py", line 23, in <module>
    print("遞歸求和:",sum_recu(1000))
  File "./sum.py", line 19, in sum_recu
    return n + sum_recu(n - 1)
  File "./sum.py", line 19, in sum_recu
    return n + sum_recu(n - 1)
  File "./sum.py", line 19, in sum_recu
    return n + sum_recu(n - 1)
  [Previous line repeated 994 more times]
  File "./sum.py", line 18, in sum_recu
    if n > 0:
RecursionError: maximum recursion depth exceeded in comparison

***解決遞歸調用棧溢出的方法是通過尾遞歸優化,事實上尾遞歸和循環的效果是一樣的,所以,把循環看成是一種特殊的尾遞歸函數也是可以的。

尾遞歸優化:http://www.open-open.com/lib/view/open1480494663229.html

二分法查找大家應該聽說過;就是一種快速查找的方法,時間復雜度低,邏輯簡單易懂,總的來說就是不斷的找出中間值,用中間值對比你需要找的實際值;若中間值大,則繼續找左邊;若中間值小,則繼續找右邊;可以看出二分法就是不斷重復此上過程,所以就可以通過遞歸方式來實現二分法查找了!

#!/usr/bin/env python3

#The binary search function

def  Binary_Search(data_source,find_n):
    if len(data_source) >= 1:                                           #判斷列表長度是否大於1,小於1就是一個值
        mid = int(len(data_source)/2)                                   #獲取列表中間索引;奇數長度列表長度除以2會得到小數,通過int將轉換整型
        if find_n > data_source[-1]:                                    #判斷查找值是否超出最大值
            print('{}查找值不存在!'.format(find_n))
            exit()
        elif find_n < data_source[0]:                                   #判斷查找值是否超出最小值
            print('{}查找值不存在!'.format(find_n))
            exit()
        if data_source[mid]  > find_n:                                  #判斷列表中間值是否大於查找值
            #print('查找值在 {} 左邊'.format(data_source[mid]))
            Binary_Search(data_source[:mid],find_n)                     #調用自己,並將中間值左邊所有元素做參數
        elif data_source[mid] < find_n:                                 #判斷列表中間值是否小於查找值
            #print('查找值在 {} 右邊'.format(data_source[mid]))           #調用自己,並將中間值右邊所有元素做參數
            Binary_Search(data_source[mid:],find_n)
        else:
            print('找到查找值',data_source[mid])                          #找到查找值
    else:
        print('{}查找值不存在!'.format(find_n))                          #特殊情況,返回查找不到

Data = [22,12,41,99,101,323,1009,232,887,97]
Data.sort()                                                             #列表從小到大排序
Binary_Search(Data,323)                                                 #查找323
執行結果:
[root@localhost tmp]# ./binary_search.py 
找到查找值 323

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM