python coding with ChatGPT 专题2| 全解递归算法

文章目录

  • 递归与栈的关系
  • 如何思考递归
    • 汉诺塔
  • 经典题目
    • 入门:斐波那契数列
    • 分治法:归并排序
    • 树的递归遍历
    • 组合问题:子集
    • 搜索问题:N皇后
  • 拓展
    • 阶乘的迭代法
    • 斐波那契数列迭代法
    • 青蛙跳
  • 参考文献

掌握递归是解决许多编程问题的关键,尤其是那些涉及树形结构、组合问题、搜索问题等领域的题目。递归方法的核心在于将问题分解成更小的子问题,直到达到一个基本情况(base case),然后再逐层返回解决整个问题。理解和掌握递归,最好的方式是通过实践一系列逐渐增加难度的问题。

递归与栈的关系

递归的过程就是出入栈的过程

我们以阶乘为例:

def Factorial(n):
    if n == 0:
        return 1
    return factorial(n-1) * n

取 n=3,则过程如下:
在这里插入图片描述 第 1~4 步,都是入栈过程,Factorial(3)调用了Factorial(2),Factorial(2)又接着调用Factorial(1),直到Factorial(0);
第 5 步,因 0 是递归结束条件,故不再入栈,此时栈高度为 4,即为我们平时所说的递归深度;
第 6~9 步,Factorial(0)做完,出栈,而Factorial(0)做完意味着Factorial(1)也做完,同样进行出栈,重复下去,直到所有的都出栈完毕,递归结束。
在这里插入图片描述

每一个递归程序都可以把它改写为非递归版本。我们只需利用栈,通过入栈和出栈两个操作就可以模拟递归的过程,二叉树的遍历无疑是这方面的代表。

如何思考递归

我们怎么判断这个递归计算是否是正确的呢?Paul Graham 提到一种方法,如下:

如果下面这两点是成立的,我们就知道这个递归对于所有的 n 都是正确的。

· 当 n=0,1 时,结果正确;
· 假设递归对于 n 是正确的,同时对于 n+1 也正确。

这种方法很像数学归纳法,也是递归正确的思考方式,上述的第 1 点称为基本情况,第 2 点称为通用情况。

在递归中,我们通常把第 1 点称为终止条件,因为这样更容易理解,其作用就是终止递归,防止递归无限地运行下去。

下面我们用1个例子来具体说明这种数学归纳法:

汉诺塔

在这里插入图片描述问题描述为:有三根杆子 A,B,C。A 杆上有 N 个穿孔圆盘,盘的尺寸由上到下依次变大,B,C 杆为空。要求按下列规则将所有圆盘移至 C 杆:

  • 每次只能移动一个圆盘;
  • 大盘不能叠在小盘上面。

问:如何移?最少要移动多少次?

首先看下基本情况,即终止条件:N=1 时,直接从 A 移到 C。

再来看下通用情况:当有 N 个圆盘在 A 上,我们已经找到办法将其移到 C 杠上了,我们怎么移动 N+1 个圆盘到 C 杠上呢?很简单,我们首先用将 N 个圆盘移动到 C 上的方法将 N 个圆盘都移动到 B 上,然后再把第 N+1 个圆盘(最后一个)移动到 C 上,再用同样的方法将在 B 杠上的 N 个圆盘移动到 C 上,问题解决。

def Hanoi(n, a='A', b='B', c='C'):
    steps = 0
    if n == 0:
        return steps
    if n==1:
        steps += 1
        print(a, '-->', c)
        return steps
    steps += Hanoi(n-1,a,c,b)
    steps += Hanoi(1,a,b,c)
    steps += Hanoi(n-1,b,a,c)
    return steps

在这里插入图片描述

经典题目

在这里插入图片描述

入门:斐波那契数列

斐波那契数列是通过如下递归式定义的:

  • F(0) = 0, F(1) = 1
  • 对于 n > 1,F(n) = F(n-1) + F(n-2)

这意味着,斐波那契数列从0和1开始,之后的每个数字都是前两个数字的和。例如,斐波那契数列的前几个数字是:0, 1, 1, 2, 3, 5, 8, 13, 21…

def fib(n):
    if n<2:
        return n
    return  fib(n-1) + fib(n-2)

在这里插入图片描述

分治法:归并排序

归并排序是一种高效的排序算法,基于分治法的一个典型应用。它将一个数组分成两半,对每部分递归地应用归并排序,然后将两个排序好的部分合并成一个最终的排序数组。

步骤:

  1. 分解:将当前区间一分为二,递归地对这两个子区间进行归并排序。
  2. 合并:将两个排序好的子区间合并成一个最终的排序区间。

实现提示
归并过程需要额外的空间来合并两个有序数组,这是归并排序空间复杂度为O(n)的原因。
实现归并排序时,考虑如何优雅地合并两个有序的子数组。
双指针法

def merge_sort(arr):
    length = len(arr)
    if length <2:
        return arr
    mid = length//2
    left_half = arr[:mid]
    right_half = arr[mid:]
    left_sort = merge_sort(left_half)
    right_sort = merge_sort(right_half)

    i = j = k = 0
    while i < len(left_sort) and j < len(right_sort):
        if left_sort[i] <= right_sort[j]:
            arr[k] = left_sort[i]
            i += 1
            k += 1
        else:
            arr[k] = right_sort[j]
            j += 1
            k += 1

    if i < len(left_sort):
        arr[k:] = left_sort[i:]
    if j < len(right_sort):
        arr[k:] = right_sort[j:]

    return arr

在这里插入图片描述

树的递归遍历

关于二叉树的深度优先所有(DFS)问题,我们在前面的文章中有详细的讲解。

前序遍历(Preorder Traversal):首先访问根节点,然后递归地做前序遍历左子树,接着递归地做前序遍历右子树。
中序遍历(Inorder Traversal):首先递归地做中序遍历左子树,然后访问根节点,最后递归地做中序遍历右子树。
后序遍历(Postorder Traversal):首先递归地做后序遍历左子树,然后递归地做后序遍历右子树,最后访问根节点。

在Python中,二叉树节点可以通过下面的TreeNode类来表示:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
def preorderTraversal(root):
    res = []
    if not root:
        return res
    res.append(root)
    res.extend(preorderTraversal(root.left))
    res.extend(preorderTraversal(root.right))
    return res

def inorderTraversal(root):
    res = []
    if not root:
        return res
    res.extend(inorderTraversal(root.left))
    res.append(root)
    res.extend(inorderTraversal(root.right))
    return res

def postorderTraversal(root):
    res = []
    if not root:
        return res
    res.extend(postorderTraversal(root.left))
    res.extend(postorderTraversal(root.right))
    res.append(root)
    return res

组合问题:子集

给定一组不同的整数,返回所有可能的子集(幂集)。解集不能包含重复的子集。

例子

输入:
nums = [1,2,3]

输出:

[  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

这个问题可以通过递归(回溯算法)来解决。我们之前的文章也讲到过回溯的理论基础和模板。本题的基本思路是遍历所有元素,对于每个元素,我们可以选择将其包含到当前子集中,或者不包含它。每当我们到达数组的末尾时,我们就找到了一个可能的子集,并将其添加到结果列表中。

def subsets(nums):
    res = []
    def backtrack(start, path):
        res.append(path[:])
        for i in range(start, len(nums)):
            path.append(nums[i])
            backtrack(i+1, path)
            path.pop()
        # return
    backtrack(0, [])
    return res

在这里插入图片描述

搜索问题:N皇后

N皇后问题简介
N皇后问题要求在一个N×N的棋盘上放置N个皇后,使得它们互不攻击。这意味着任何两个皇后都不能处于同一行、同一列或同一对角线上。

解题思路
这个问题可以通过回溯法解决,核心思想是逐行放置皇后,并在每一行中尝试所有列,直到找到一个合法的位置。我们需要一个方法来检查在放置每个新的皇后时,棋盘的状态是否仍然合法。

在这里插入图片描述

def solveNQueens(n):
    board = [["."] * n for _ in range(n)]
    res = []

	def isValid(board, row, col):
	    for i in range(row):
	        # 检查列是否合法
	        if board[i][col] == 'Q':
	            return False
	        # 检查左上对角线
	        if col - i - 1 >= 0 and board[row - i - 1][col - i - 1] == 'Q':
	            return False
	        # 检查右上对角线
	        if col + i + 1 < n and board[row - i - 1][col + i + 1] == 'Q':
	            return False
	    return True


    def backtrack(board, row):
        if row == n:
            res.append(["".join(row) for row in board])
            return
        for col in range(n):
            if not isValid(board, row, col):
                continue
            board[row][col] = "Q"
            backtrack(board, row + 1)
            board[row][col] = "."

    backtrack(board, 0)
    return res

拓展

迭代法可以避免递归,并且在实际应用中,迭代通常比递归有更好的性能,尤其是在防止栈溢出方面。

阶乘的迭代法

def factorial(n):
    if n == 0:
        return 1
    pre = 1
    for i in range(2, n+1):
        result = i * pre
        pre = result
    return result

斐波那契数列迭代法

def fib(n):
    if n==0:
        return 0
    if n==1:
        return 1
    pre_n_1 = 1
    pre_n_2 = 0
    for i in range(2,n+1):
        result = pre_n_1 + pre_n_2
        pre_n_2 = pre_n_1
        pre_n_1 = result
    return result

青蛙跳

一只青蛙可以一次跳 1 级台阶或者一次跳 2 级台阶,例如:

跳上第 1 级台阶只有一种跳法:直接跳 1 级即可。 跳上第 2 级台阶有两种跳法:每次跳 1 级,跳两次;或者一次跳 2 级。 问要跳上第 n 级台阶有多少种跳法?

递归法

def jump(n):
    if n <= 2:
        return n
    return jump(n - 1) + jump(n - 2)

迭代法

def jump(n):
    if n <= 2:
        return n
    pre_n_1 = 2
    pre_n_2 = 1
    for i in range(3,n+1):
        result = pre_n_1 + pre_n_2
        pre_n_2 = pre_n_1
        pre_n_1 = result
    return result

参考文献

一文读懂递归算法
一文看懂什么是递归

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/511573.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VBA数据库解决方案第九讲:把数据库的内容在工作表中显示

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

如何使用极狐GitLab 启用自动备份功能

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何极狐GitLab 自…

HarmonyOS 和 OpenHarmony

HarmonyOS 和 OpenHarmony 支持的 shell 命令不同&#xff0c;因此有时候需要做一做区分&#xff0c;目前有些文档上没有标注&#xff0c;因此可能产生歧义。 HarmonyOS 支持 getprop&#xff1a; getprop hw_sc.build.os.apiversion # 查看API版本OpenHarmony 上支持 param…

2024年NAND价格市场继续上涨

TrendForce发布了最新的NAND闪存市场价格走势预测。根据其报告&#xff0c;在2024年第二季度&#xff0c;NAND闪存合同价格将进一步呈现两位数的增长&#xff0c;叠加前一季度的增长。不过&#xff0c;客户端SSD的价格涨幅预计在第二季度将不超过15%&#xff0c;相比于2024年第…

破解密码:掌握2024年的营销归因

Cracking the Code: Mastering Marketing Attribution in 2024 营销归因是识别哪些营销渠道和触及点有助于销售或转化的过程。随着消费者继续通过多个渠道与品牌互动&#xff0c;掌握营销归因对企业来说变得越来越重要。在这篇文章中&#xff0c;我们将探讨破解代码和有效衡量…

PW1503限流芯片:可达3A限流,保障USB电源管理安全高效

在电源管理领域&#xff0c;开关的性能直接关系到设备的稳定性和安全性。今天&#xff0c;我们将详细解析一款备受关注的超低RDS&#xff08;ON&#xff09;开关——PW1503。它不仅具有可编程的电流限制功能&#xff0c;还集成了多项保护机制&#xff0c;为各类电子设备提供了高…

vue两个特性和什么是MVVM

一、什么是vue 1.构建用户界面 用vue往html页面中填充数据&#xff0c;非常的方便 2.框架 框架是一套线成的解决方案 vue的指令、组件&#xff08;是对ui结构的复用&#xff09;、路由、vuex 二、vue的特性 1.数据驱动视图 2.双向数据绑定 1.数据驱动视图 数据的变化会驱动…

基于tensorflow和kereas的孪生网络推理图片相似性

一、环境搭建 基础环境&#xff1a;cuda 11.2 python3.8.13 linux ubuntu18.04 pip install tensorflow-gpu2.11.0 验证&#xff1a;# 查看tensorflow版本 import tensorflow as tf tf.__version__ # 是否能够成功启动GPU from tensorflow.python.client import device_lib pr…

Navicat for MySQL 15免费注册方法

一、效果图如下&#xff1a; 注&#xff1a;此方法仅用于非商业用途&#xff0c;请勿传播&#xff0c;否则后果自负。 二、下载安装 下载安装包&#xff0c;分为32位和6位&#xff0c;下载文件名&#xff1a;Navicat for MySQL 15.zip&#xff08;https://download.csdn.net/…

Prometheus+grafana环境搭建redis(docker+二进制两种方式安装)(四)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前三篇 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabbitmq(docker二进制两种方式安装)(二)-CSDN博客 Prometheusgrafana环境搭建m…

Nginx反向代理和缓存

一、Nginx反向代理 1.调度和代理的区别&#xff1a; 1.调度基于内核层面&#xff0c;代理基于应用层面 2.代理必须实现一手托两家 3.调度不需要监听任何端口&#xff0c;不需要工作任何应用程序&#xff0c;代理需要工作和上游服务器一模一样的进程 4.调度没有并发上限&am…

CentOS7安装flink1.17完全分布式

前提条件 准备三台CenOS7机器&#xff0c;主机名称&#xff0c;例如&#xff1a;node2&#xff0c;node3&#xff0c;node4 三台机器安装好jdk8&#xff0c;通常情况下&#xff0c;flink需要结合hadoop处理大数据问题&#xff0c;建议先安装hadoop&#xff0c;可参考 hadoop安…

nslookup查询网站是否支持IPV6

nslookup是一种网络管理命令行工具&#xff0c;可用于查询DNS域名和IP地址输入指令nslookup默认服务器和Address是当前上网所用的DNS服务器域名和地址A记录A&#xff08;Address&#xff09;记录指的是用来指定主机名或域名对应的IP记录。

java数据结构与算法刷题-----LeetCode547. 省份数量

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 深度优先遍历广度优先遍历 本题考察图的连通分量个数。也就是所有…

24/04/02总结

API: bigdecima: 方法名 说明 public static BigDecimal valueof( double val) 静态获取对象 public BigDecimal add(BigDecimal val) 加法 public BigDecimal subtract(BigDecimal val…

JavaScript库,编写$()和getElementsByClassName()方法

背景: JavaScript库是一组预先编写好的JavaScript代码集合&#xff0c;旨在简化常见的网页开发任务。这些库通常包含了许多函数和方法&#xff0c;可以帮助开发人员处理各种任务&#xff0c;比如DOM操作、事件处理、动画效果、AJAX请求等等。使用JavaScript库可以节省开发时间…

Python 与机器学习,在服务器使用过程中,常用的 Linux 命令包括哪些?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 本博客旨在分享在实际开发过程中&#xff0c;开发者需要了解并熟练运用的 Linux 操作系统常用命令。Linux 作为一种操作系统&#xff0c;与 Windows 或 MacOS 并驾齐驱&#xff0c;尤其在服务器和开发环…

Redis缓存设计与性能优化【缓存和数据库不一致问题,解决方案:1.加过期时间这样可以一段时间后自动刷新 2.分布式的读写锁】

Redis缓存设计与性能优化 缓存与数据库双写不一致 缓存与数据库双写不一致 在大并发下&#xff0c;同时操作数据库与缓存会存在数据不一致性问题 1、双写不一致情况 2、读写并发不一致 解决方案&#xff1a; 1、对于并发几率很小的数据(如个人维度的订单数据、用户数据等)&a…

Spring中BeanFactoryPostProcessor详解

目录 功能与作用 使用案例 spring提供的常见BeanFactoryPostProcessor 1.EventListenerMethodProcessor 2.BeanDefinitionRegistryPostProcessor 功能与作用 使用案例 spring提供的唯一BeanDefinitionRegistryPostProcessor 总结 功能与作用 参考BeanFactoryPostProce…

FebHost:人工智能时代的新宠儿.AI域名

近年来,人工智能技术在各行各业迅猛发展,正在深刻改变着我们的生活。作为AI领域的专属域名,.AI域名正成为越来越多企业和个人的首选。 那么,.AI域名到底是什么呢?它是一种特殊的顶级域名(Top-Level Domain, TLD),于2013年由 安哥拉政府正式退出。与其他通用顶级域名如.com、.…