Python学习_基础_17_函数递归

本文详细介绍了Python中函数递归的概念,包括直接和间接递归,并通过示例解释了递归的本质是循环。同时,通过员工薪水问题展示了递归的回溯和递推两个阶段。此外,还探讨了递归在解决实际问题中的应用,如遍历多层列表和实现二分查找算法,强调了递归函数必须有明确的结束条件以避免无限循环。最后,通过二分查找的例子说明了在非有序列表中可能需要先进行排序的情况。
摘要由CSDN通过智能技术生成

Python-函数递归

一、递归的定义

函数的递归调用:是函数嵌套调用的一种特殊形式,具体是指在调用一个函数的过程中又直接或者间接地调用到函数本身

直接调用本身

def f1():
    print('是我是我还是我')
    f1()


f1()

间接调用本身

def f1():
    print('===>f1')
    f2()


def f2():
    print('===>f2')
    f1()


f1()

像上面两种情况,会无限循环调用下去,导致死循环。所以Python对函数循环调用的次数做了要求,最大为1000次。虽然支持可以更改最大循环调用次数,但最好不要。

所以递归的本质就是循环

至此,我们知道了一段代码的循环运行方式有两种

方式一:while、for循环

while True:
    print(1111)
    print(2222)
    print(3333)

方式二:递归

def f1():
    print(1111)
    print(2222)
    print(3333)
    f1()
    
f1()
需要强调的的一点是:递归调用不应该无限地调用下去,必须在满足某种条件下结束递归调用(使用return)。
n=0
while n < 10:
    print(n)
    n+=1


def f1(n):
    if n == 10:
        return
    print(n)
    n+=1
    f1(n)

f1(0)

二、递归调用的两个阶段[参考文章]

  1. 回溯:未满足结束条件时,一直进行递归调用,一层一层调用下去。
  2. 递推:满足某种结束条件,结束递归调用,然后一层一层返回。
举例说明:

某公司四个员工坐在一起,问第四个人薪水,他说比第三个人多1000,问第三个人薪水,第三个人他说比第二个人多1000,问第二个人薪水,他说比第一个人多1000,最后第一人说自己每月5000,请问第四个人的薪水是多少?

要知道第四个人的月薪,就必须知道第三个人的,第三个人的又取决于第二个人的,第二个人的又取决于第一个人的,而且每一个员工都比前一个多一千,数学表达式即:

salary(4) = salary(3) + 1000 
salary(3) = salary(2) + 1000 
salary(2) = salary(1) + 1000 
salary(1) = 5000
总结为: 
salary(n) = salary(n-1) + 1000 (n>1) 
salary(1) = 5000 (n=1) 

在回溯阶段:要得到第n个员工的薪水,需要回溯得到第(n-1)个员工的薪水,以此类推,直到得到第一个员工的薪水。

此时,salary(1)已知(满足结束条件),因而不必再向前回溯了。

然后进入递推阶段:从第一个员工的薪水可以推算出第二个员工的薪水(6000),从第二个员工的薪水可以推算出第三个员工的薪水(7000),以此类推,一直推算出第第四个员工的薪水(8000)为止,递归结束。

需要注意的一点是,递归一定要有一个结束条件,这里n=1就是结束条件。

图解分析:

在未满足n==1的条件时,一直进行递归调用,即一直回溯,见图解分析的左半部分。

而在满足n==1的条件时,终止递归调用,即结束回溯。从而进入递推阶段,依次推导直到得到最终的结果。

代码实现:
def salary(n):
    if n == 1:
        return 5000
    return salary(n - 1) + 1000


print(salary(4))  # 8000

三、函数递归调用的案例

有一个嵌套多层的列表,要求打印出所有的元素。
l = [1, [2, [3, [4, [5, [6, [7, [8, 9]]]]]]]]


def f1(list1):
    for x in list1:
        if type(x) is list:
            # 如果是列表,应该再循环、再判断,即重新运行本身的代码
            f1(x)
        else:
            print(x)
            
f1(l)

二分法

需求:有一个按照从小到大顺序排列的数字列表,需要从该数字列表中找到我们想要的那个一个数字,如何做更高效?

nums = [-3, 4, 7, 10, 13, 21, 43, 77, 89]
find_num = 10
方案一:整体遍历(效率太低)
for num in nums:
    if num == find_num:
        print('find it')
        break
方案二:二分法

思路:将查找的目标值与列表中间值进行比较,若目标值比中间值大,则在中间值的右边部分继续找;若目标值比中间值小,则在中间值的左边部分继续找。

利用这种方法,每次都与中间值进行比较来确定范围,会大大减少程序运行时间。

def binary_search(find_num, 列表):
    mid_val = 找列表中间的值
    if find_num > mid_val:
        # 接下来的查找应该是在列表的右半部分
        列表 = 列表切片右半部分
        binary_search(find_num, 列表)
    elif find_num < mid_val:
        # 接下来的查找应该是在列表的左半部分
        列表 = 列表切片左半部分
        binary_search(find_num, 列表)
    else:
        print('find it')

列表中间值怎么找?==》找中间索引

mid_index = len(l) // 2  # 长度除2取整
mid_value = l[mid_index]

# 无关于列表的元素个数是奇数还是偶数,目的至是为了找一个值来对整体列表进行切割。

代码实现

nums = [-3, 4, 7, 10, 13, 21, 43, 77, 89]
find_num = 10


def binary_search(find_num, l):
    print(l)
    if len(l) == 0:
        print('找的值不存在')
        return

    mid_index = len(l) // 2
    if find_num > l[mid_index]:
        # 接下来的查找应该是在列表的右半部分
        l = l[mid_index + 1:]  # 顾头不顾尾原则,和l[mid_index]已经比较过了,没有保留它的必要。
        binary_search(find_num, l)
    elif find_num < l[mid_index]:
        # 接下来的查找应该是在列表的左半部分
        l = l[:mid_index]
        binary_search(find_num, l)
    else:
        print('find it')


binary_search(find_num, nums)
# [-3, 4, 7, 10, 13, 21, 43, 77, 89]
# [-3, 4, 7, 10]
# [10]
# find it

特殊情况:找一个不存在的数

nums = [-3, 4, 7, 10, 13, 21, 43, 77, 89]
find_num = 99


def binary_search(find_num, l):
    print(l)
    if len(l) == 0:
        print('找的值不存在')
        return

    mid_index = len(l) // 2
    if find_num > l[mid_index]:
        # 接下来的查找应该是在列表的右半部分
        l = l[mid_index + 1:]  # 顾头不顾尾原则,和l[mid_index]已经比较过了,没有保留它的必要。
        binary_search(find_num, l)
    elif find_num < l[mid_index]:
        # 接下来的查找应该是在列表的左半部分
        l = l[:mid_index]
        binary_search(find_num, l)
    else:
        print('find it')


binary_search(find_num, nums)
# [-3, 4, 7, 10, 13, 21, 43, 77, 89]
# [21, 43, 77, 89]
# [89]
# []
# 找的值不存在

如果列表元素不是由小到大排列的呢?

使用sort方法让列表元素按照由小到大的顺序排列。

nums = [-3, 4, 1, 5, 2, 0, 1, 3]
nums.sort()
print(nums)  # [-3, 0, 1, 1, 2, 3, 4, 5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值