zoukankan      html  css  js  c++  java
  • 递归和二分法

    1. 什么是递归

    递归实际上函数的递归,而函数递归就是函数嵌套调用的一种特殊方式

    具体是指:在调用一个函数的过程中又直接或间接的调用了函数本身

    1.1 直接调用

    举例 : 在调用func1的过程中,又调用了func1,这就是直接调用函数本身

    def func1():
        print("我是func1====>")
        func1()
        
    func1()
    # 首先调用func1,然后函数体内部代码再调用func1,虽然局部作用域中没有func1这个名字,但是可以从全局
    # 作用域获得
    

    image-20201207190340703

    1.2 间接调用

    举例 : 在调用func1的过程中,又调用了func2,func2中又调用func1 , 这就是间接调用

    def func1():
        print("我是func1====>")
        func2()
        
        
    def func2():
        print("我是func2====>")
        func1()
        
        
        
    func1()
    

    image-20201207191035907

    递归就是在自己调用自己,实际上递归的本质就是循环

    2. 一段代码的循环执行

    之前我们讲过循环的两种方式,一种while循环,一种for循环,现在又多了一种方式,就是递归

    2.1 方式一

    while True:
        print(1111)
        print(2222)
        print(3333)
        
    # 在循环体中放入要循环的代码
    

    2.2 方式二

    def func():
    	print(1111)
    	print(2222)
    	print(3333)    # 函数体中写入要循环执行的代码
    	func()
        
        
    func()
    

    2.3 注意

    递归可以实现的while True都可以实现

    递归不能无限循环下去,因为递归是在调用函数,你调用函数就要创建局部名称空间用来存放名字,如果你一直递

    归,也就是要一直创建名称空间,会消耗大量的内存,如果不加以限制,会造成内存溢出。所以应该在满足某种

    条件下,结束递归,即结束函数的调用。

    #1. 可以使用sys.getrecursionlimit()去查看递归深度,默认值为1000,虽然可以使用
    # sys.setrecursionlimit()去设定该值,但仍受限于主机操作系统栈大小的限制
    
    #2. python不是一门函数式编程语言,无法对递归进行尾递归优化。
    #尾递归优化就是在调用下一次自己的时候,把上一次调用产生的名称空间回收了,但是python做不到
    

    3. 回溯和递推

    我们将用一个小学算年龄的问题来解释递归的原理和使用

    # 问题:
    有五个人,问第5个人他说我比第4个人大5岁,问第4个人他说我比第3个人大5岁,问第3个人他说我比第2个人大5岁,问第2个人他说我比第1个人大5岁,问第1个人他说他10岁,问1,2,3,4,5各几岁?
    

    伪代码:

    age(5) = age(4) + 5
    age(4) = age(3) + 5
    age(3) = age(2) + 5
    age(2) = age(1) + 5
    age(1) = 10
    
    总结:age(n) = age(n-1) + 5
    

    在回溯阶段,要求第n个人的年龄,需要回溯得到(n-1)个人的年龄,以此类推,直到得到第一个人的年龄,此

    时,age(1)已知,因而不必再向前回溯了。然后进入递推阶段:从第一个人的年龄可以推算出第二个人的年龄

    15,从第二个人的年龄可以推算出第三个人的年龄20,以此类推,一直推算出第五个人的年龄30为止,递归结

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

    def age(n):
        if n == 1:
            return 10
        return age(n-1) + 5
    
    
    #   return age(1) + 5 + 5 + 5 + 5
    

    image-20201207195448639

    递归的应用场景

    当你遇到一种需求中 , 有功能是需要简单的重复实现的话 , 就可以把这这个功能定义一个函数 , 然后在内部自己调

    用自己 , 实现功能的重复

    4. 小练习

    使用递归迭代打印出列表中的所有值

    l = [1, 2, 3, [1, 2, [1, 2, [4, 5, 6, [7, 8, [10, 11, 12, [13, 14, 15],7,8,9],18,20],21]]]]
    
    
    def func(l):
        for i in l:
            if type(i) == list:
                l = i
                func(l)
            else:
                print(i)
    
    func(l)
    

    5. 二分法

    二分法是一种算法,所谓算法就是一种高效解决问题的方法,二分法常用来快速查找数据

    lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 999, 11, 12, 13, 14]
    
    
    def binary_search(find_num, l):
        print(l)
        if not len(l):
            print('要找的值不存在')
            return
        mid_val_index = len(l) // 2
        mid_val = l[mid_val_index]          # 当列表为空,再用索引取值就报错
        if find_num > mid_val:
            # 接下来的查找应该是在列表的右半部分
            l = l[mid_val_index + 1:]       # 切片超出索引会得到有个空列表
            binary_search(find_num, l)
        elif find_num < mid_val:
            # 接下来的查找应该是在列表的左半部分
            l = l[:mid_val_index]
            binary_search(find_num, l)
        else:
            print('find it')
    
    
    
    binary_search(999, lst)
    # 如果你遍历要找9次,但是二分法,3次就找到了
    
  • 相关阅读:
    LeetCode–打印从1到最大的n位数
    常用十大算法(十)— 踏棋盘算法
    常用十大算法(九)— 弗洛伊德算法
    常用十大算法(八)— 迪杰斯特拉算法
    LeetCode–组合
    LeetCode–组合总和
    5513. 连接所有点的最小费用 kruskal
    152. 乘积最大子数组 dp
    1567. 乘积为正数的最长子数组长度 dp
    5481. 得到目标数组的最少函数调用次数. 位运算
  • 原文地址:https://www.cnblogs.com/xcymn/p/14107791.html
Copyright © 2011-2022 走看看