1.递归

本文介绍了递归的概念及其在斐波那契数列计算中的应用,展示了递归和循环两种方式的优缺点。同时,探讨了二分查找算法的非递归和递归实现,以及汉诺塔问题的解决方法。递归在编程中虽然简洁,但可能导致效率低下和栈溢出问题,而循环则能有效避免这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1递归

什么是递归?递归不是算法,函数内部调用函数本身。
递归一定要终止,怎么写终止条件很重要。

1.1斐波拉契数列

斐波拉契数列又称为黄金分割数列,因为数学家列昂纳多 斐波那契以兔子繁殖为列子而引入,故又称为“兔子数列”。
指的是这样一个数列:1、1、2、3、5、8、13、21、34、…后面的数都等于前面的数的和。

1.2斐波那契数列的计算

  • 递归求解
from collections import defaultdict=
total = {}
total = defaultdict(int)

def fib_test(k):
    # 递归求解第k个数的值
    if k in [1, 2]:
        return 1
    global total
    total[k] += 1
    return fib_test(k - 1) + fib_test(k - 2)


if __name__ == "__main__":
    from datetime import datetime
    start_time = datetime.now()
    fib_test(35)# 迭代9227464次
    fib_test(34)  # 迭代5702886次
    # 递归由于重复计算的次数非常多,因此特别耗时
    print("递归耗时:{}".format((datetime.now() - start_time).total_seconds()))
    print(total)

  • 循环求解
def fib_test2(k):
    # 循环求解第k个数的值
    assert k > 0, "k必须大于0"
    if k in [1, 2]:
        return 1
    k_1 = 1
    k_2 = 1
    for i in range(3, k + 1):
        temp = k_1
        k_1 = k_1 + k_2
        k_2 = temp
    return k_1


if __name__ == "__main__":
    from datetime import datetime
    start_time = datetime.now()
    print(fib_test2(100))  # 迭代5702886次
    print("循环耗时:{}".format((datetime.now() - start_time).total_seconds()))
    print(total)

1.3斐波那契数列的其他问题

  • 1.爬楼梯问题
    假设你现在在爬楼梯,楼梯有n阶,每次你只能爬1级或2级,那么你有多少种方法爬到楼梯的顶部。
    分析:对于顶部而言,倒数第一步只有两种到达方式,爬1级或爬2级。
  • 2.生兔子问题
    有一对兔子,从出生第3个月起,每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假设兔子都不死,问每个月的兔子总数为多少?
    分析:
    第一个月 2=2
    第二个月 2=2
    第三个月 2+2(新)=4
    第四个月 2+2(新1)+2(新)=6
    第五个月 2+2(新)+2(新1)+2(新2) = 8
    n>3
    f(n) = f(n-1) + f(n-3)

1.4二分查找-非递归实现

二分查找的效率高,时间复杂度为O(log_2⁡n ),即O(log⁡n )

# data = [1, 7, 17, 18, 27, 29, 30, 35, 35, 39, 41, 63, 63, 66, 67, 78, 82, 82, 91, 92]
data = [1, 7, 17, 18, 27, 29, 30, 35, 39, 41, 63, 66, 67, 78, 82, 91, 92]
# 二分查找(折半查找)
# 假设有32个数,每运行一次,少一半。32->16->8->4->2
def search(data_list, target):
    left = 0
    right = len(data_list) - 1
    target_pos = -1
    while left <= right:
        # 1.找到[left,right]中间的值,int()向下取整
        mid = int((left + right) / 2)
        # 2. 判断中间位置的值和目标值的大小
        if data_list[mid] == target:
            target_pos = mid
            break
        elif data_list[mid] < target:
            # 如果中间值小于目标值,则在右侧继续二分查找
            left = mid + 1
        else:
            # 如果中间值大于目标值,则在左侧继续二分查找
            right = mid - 1
    return target_pos


if __name__ == "__main__":
    pos = search(data, 92)
    if pos >= 0:
        print("查询到数据,所在位置 :{}".format(pos))
    else:
        print("目标值不在列表中")

1.5二分查找-递归实现

import random
# data = [1, 7, 17, 18, 27, 29, 30, 35, 39, 41, 63, 66, 67, 78, 82, 91, 92]

def random_list(start, end, length):
    # 随机生成长度为length,从start到end之间的列表
    data_list = []
    for i in range(length):
        data_list.append(random.randint(start, end))
return data_list


def search(left, right, data_list, target):
    if left > right:
        return -1
    mid = int((left + right) / 2)
    if data_list[mid] == target:
        return mid
    elif data_list[mid] < target:
        # 如果中间值小于目标值,则继续在右侧二分查找
        return search(mid + 1, right, data_list, target)
    else:
        # 如果中间值大于目标值,则继续在左侧二分查找
        return search(left, mid - 1, data_list, target)

if __name__ == "__main__":
    # pos = search(0, len(data) - 1, data, 29)
    # if pos >= 0:
    #     print("查询到数据,所在位置 :{}".format(pos))
    # else:
    #     print("目标值不在列表中")
    # 随机生成一组数,并排序
    data = random_list(1, 100, 10)
    data = sorted(data)
    print(data)
    target = random.randint(0, len(data) - 1)
    print(data[target])
    pos = search(0, len(data) - 1, data, data[target])
    if pos >= 0:
        print("查询到数据,所在位置 :{}".format(pos))
    else:
        print("目标值不在列表中")
        

1.6汉诺塔实现

# 1.大傻:只搬动一号盘
# 2.1.叫谁来做;2.从哪个柱子搬到哪个柱子,中间柱子
# 2.2.搬动自己负责的柱子 3. 叫刚刚那个谁从中间柱子个柱子搬到目标柱子

def move(index, start, mid, end):
    if index == 1:
        print("{}-->{}".format(start, end))
        return
    else:
        move(index - 1, start, end, mid)
        print("{}-->{}".format(start, end))
        move(index - 1, mid, start, end)


if __name__ == "__main__":
    move(3, "A", "B", "C")

运行结果:在这里插入图片描述

1.7总结

  • 递归优点:
    1.写起来简单。
    2.递归都能通过非递归的方式完成。
  • 递归缺点:
    1.递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址一级临时变量,而往栈中压入数据和弹出数据都需要时间。效率不高。
    2.递归很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在互相重叠的部分,则存在重复计算,如Fibonacci斐波那契数列的递归实现。效率不高
    3.调用栈可能会溢出,其实每一次函数调用会在内存中分配空间,而每个进程栈的容量是有限的,当调用的层次太多,就会超出栈的容量,从而导致栈溢出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值