编程集训第5天:递归及DP思想,温习前4天内容

本文深入探讨递归与动态规划(DP)的核心概念,递归作为一种自我调用的函数设计方法,以及DP在解决复杂问题时的高效应用。文章通过实例讲解二叉树的遍历与层次遍历,对比递归的简洁与潜在栈溢出风险,以及DP在重叠子问题中的优势。

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

编程集训第5天:递归及DP思想,温习前4天内容

递归及DP思想

1、递归(Recursion)
指在函数的定义中使用函数自身的方法,即程序的自身调用。

递归一词还较常用于描述以自相似方法重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。递归就是方法里调用自身。

出口:在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。

效率:递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。

栈溢出:在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

2、动态规划(DP)
动态规划(英语:Dynamic programming,DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。

动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。

dp的分类:分的这几类主要是一些状态比较容易表示,转移方程比较好想,问题比较基本常见的。主要包括递推、背包、LIS(最长递增序列),LCS(最长公共子序列),下面针对这几种类型,推荐一下比较好的学习资料和题目。

学习资料和分类的题目

回顾:

二叉树

其实二叉树的前 中 后序遍历采用的就是递归思想。

题目回顾:98. 验证二叉搜索树

题目:给定一个二叉树,判断其是否是一个有效的二叉搜索树。

思想:采用中序遍历的方法重新做了一遍,思路是检测当前节点的左子节点是否有节点,如果有,左子节点成为当前节点进行递归;如果是空,将当前节点添加到res中。再将前节点的右子节点称为当前节点进行递归。最后检测中序遍历好的list是否是个递增序列。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
        
    def zhongxubianli(self,root):
        if root == None:
            return []
        res = []
        res += self.zhongxubianli(root.left)
        res.append([root.val])
        res += self.zhongxubianli(root.right)
        return res      
    
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        list = self.zhongxubianli(root)
        for i in range(len(list)-1):
            if list[i] >=list[i+1]:
                return False
            
        return True          

题目回顾:102.二叉树的层次遍历

题目:给定一个二叉树,返回其按层次遍历的节点值。(即逐层地,从左到右访问所有节点)。

思路:学习其他小伙伴的解法,方法一:bfs广度优先搜索;方法二:队列思想。

方法一:bfs广度优先搜索

传递函数的输入元素有 root节点,depth深度,res储存按层级输出的数组。

从0层级开始,depth为0,如果res的数组数小于depth+1(当前是1),在res里插入一个空[],然后将当前节点存入res[0]里面;

然后将左子节点作为当前节点递归,depth加1(depth=1),直到左子节点为空。再到depth=1的右子节点作为当前节点递归。

实际上是储存进res是通过depth将每一层的节点区分开,所以与每个节点遍历的先后顺序无关了。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        res = []
        self.f1(root, 0,res)
        return res
    def f1(self, root, depth, res):
        if root == None:
            return
        if len(res) < depth+1:               #建立该层的空元素数组
            res.append([])
        res[depth].append(root.val)         #将层中元素加入该数组中
        self.f1(root.left, depth+1, res)
        self.f1(root.right, depth+1, res)

原文:https://blog.youkuaiyun.com/qq_42904396/article/details/85168544

心得:

函数的调用: self.f1

递归循环传递res,但是有个疑惑是为什么在其中一次结束return时,为什么res还能一直更新为循环时候的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值