[Python学习日记-34] 一篇文章让你弄懂 Python 中牛逼的递归函数
[Python学习日记-34] 一篇文章让你弄懂 Python 中牛逼的递归函数
简介
执行过程
特性
练习
简介
递归函数是指在函数的定义中调用函数自身的一种方式。在 Python 中,任何函数都可以是递归函数。
执行过程
在这里我们需要求100不断除以2直到商为0为止,打印每次除的商,我们分别用循环和递归来实现这一需求下面我们先来看用循环如何实现,代码如下
n = 100
while n > 0:
print(n)
n = int(n/2)
代码输出如下:
那如果用函数应该如何实现呢?我们来看看下面的代码
def calc(n):
print(n)
n = int(n / 2) # 50
if n > 0:
calc(n) # 50
calc(100)
代码输出如下:
从上面的输出效果来看,使用函数递归同样可以实现同样的效果,通俗的来讲其实递归就是自己调用自己的一种方式,从而可以达到循环的效果。是不是这个时候就以为结束了?太年轻了,如果这就是递归的话那样他完全没有必要存在,下面我们加一行代码,你将会看到一个神奇的景象,代码如下
def calc(n):
print(n)
n = int(n / 2)
if n > 0:
calc(n) # n = 50时进入的,现在执行完毕继续执行下面的代码
print(n) # 50
calc(100)
代码输出如下:
上面的输出是否震惊了你,这到底是怎么回事!为什么在递归调用自己之后加入这一行代码就会反着输出呢?这里我们就要来说说递归的一个过程了,如下图所示
如上图所示,函数在每进入下一层的时候,当前层的函数并未结束,它必须等它调用的下一层函数执行结束返回后才能继续往下走。所以最下面的那句 print(n) 会等最里层的函数执行时才会执行,然后不断往外退层,所以会出现0、1、3、6、12、25、50的效果。
特性
如果递归函数没有一个明确的结束条件就相当于一个死循环,Python 默认递归1000次就会产生报错,防止内存溢出,报错如下
递归在特定场景下还是挺有用的,在后面的一些算法就得用到递归,比如堆排、快排等,现在看还是有些复杂的,想要实现这些算法要先完全了解递归的执行过程先。
练习
一、题目
用递归实现二分查找的算法,以从列表 a=[1,3,4,6,7,8,9,11,15,17,19,21,22,25,29,33,38,69,107] 查找指定的值。
提示:二分法是一种在有序数组中搜索目标元素的算法。它的基本思想是将数组分成两部分,然后确定目标元素可能存在的那一部分,并继续在该部分中查找,直到找到目标元素或确定目标元素不存在为止。
二、答案
a = [1,3,4,6,7,8,9,11,15,17,19,21,22,25,29,33,38,69,107]
def binary_search(start,end,n,d_list):
"""
每次把列表规模折半,查找一个数据最多只需要2的n次方 < len(d_list),是2的多少次方,就是最多查多少次。
假如列表长度为200,那最多只需查询8次(2**8次方)
:param start: 查找的起始位置
:param end: 查找的结束位置
:param n: 要查找的值
:param d_list: 要找的列表
:return:
"""
if start < end: # 查找的范围[start:end]依然大于0个
mid = (start + end)//2 # 找到中间位置
if d_list[mid] > n: # 如果中间的这个值比要找的n大,代表要往d_list[mid]左边找
print("go left",start,mid,end,"--",d_list[start],d_list[mid],d_list[end-1])
binary_search(start,mid,n,d_list)
elif d_list[mid] < n : # 要往右边找,继续折半
print("go right..",start,mid,end,"--",d_list[start],d_list[mid],d_list[end-1])
binary_search(mid+1,end,n,d_list)
else: # 找到了
print("find:",d_list[mid],mid)
else: # 假设start=9,end=9, 那d_list[9:9]已经取不到值了,在这种情况下,只能说明,要找的这个值不在这个列表里
print("cannot find %s in this data list" % n)
binary_search(0, len(a), 6, a)
代码输出如下:
作者:JoveZou