递归及DP

本文探讨了递归的概念,包括其特点和应用,如斐波那契数列。接着介绍了动态规划,讲解了动态规划的最优子结构、边界条件和状态转移,并以走楼梯和最大子序和问题为例进行阐述。最后提到了LeetCode中的整数拆分问题,并分析了如何运用动态规划优化解题策略。

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

递归

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

递归算法解决问题的特点:
(1)递归就是在过程或函数里调用自身
(2)在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低,所以一般不提倡用递归算法设计程序。
(4)在递归调用的过程中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出等。

斐波拉契数列

def fib(x):
    if x < 2:
        return 0 if x == 0 else 1
    # 当x > 2时,开始递归调用fib()函数:
    return fib(x - 1) + fib(x - 2)

print(fib(6))  # 打印结果为:8

动态规划

通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。

走楼梯问题
有十个台阶,从上往下走,一次只能走一个或两个台阶,请问总共有多少种走法?
1、最优子结构:我们来考虑要走到第十个台阶的最后一步,最后一步必须走到第八或者第九。不难得到 f(10) = f(9)+f(8)。f(9) = f(8)+f(7)
2、边界:f(1) = 1, f(2) = 2
3、状态转移:f(n) = f(n-1) + f(n-2)

解法一:递归
def get_count(n):
    if n == 1:return 1
    if n == 2:return 2
    else:
        return get_count(n-1)+get_count(n-2)
print(get_count(10))

在这里插入图片描述
很显然这是一个满二叉树,高度为N-1。所以总节点数为2N-1,时间复杂度为O(2N)
回顾一下上面的递归计算方法,我们不难看出,本来总共只有f(1)-f(N)个节点,硬生生被增加到2^N-1个,这就是产生了大量重复的运算。找到问题的根源,对应的解决方法就应运而生,那就是从下往上算,把以前计算过的数值,保存在一个哈希表中,然后后面计算时先查询一下,存在就无需计算。时间复杂度为O(n) ,空间复杂度为O(n)。但是在仔细一想其实,无需保存所有的f,每个f都只与前两个值相关,所以空间复杂度可以降低为O(1).我们来看看相关代码。

def get_count(n):
    if n == 1:return 1
    elif n == 2 :return 2
    else:
        l = [1,2]
        for i in range(3,n):
            l[0],l[1] = l[1],l[0]+l[1]
        return l[0]+l[1]

LeetCode

343 整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。

思路:
自顶向下的思考:第n个数对应拆分结果为,in-i的拆分结果 和 in-i的最大值。
得到了对应的状态转移方程:F(n) = MaX(i*(n-i) , iF(n-i))其中I = (1到 n-1)
反推自底向上。F(1) = 1 F(2) = 1 …F(n) = MaX(i
(n-i)

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(n<=1)
            return 0;
        
        int[] min = new int[n];
        min[0] = prices[0];
        int res = 0;
        for(int i= 1 ; i<n ; i++){
            if(prices[i] > min[i-1])
                res = (int)Math.max(prices[i]-min[i-1],res);
            min[i] = (int)Math.min(min[i-1] , prices[i]);
        }
        
        return res;
    }
}

53.最大子序和
给定一个序列(至少含有 1 个数),从该序列中寻找一个连续的子序列,使得子序列的和最大。
例如,给定序列 [-2,1,-3,4,-1,2,1,-5,4],
连续子序列 [4,-1,2,1] 的和最大,为 6。

class Solution:
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        sum = 0
        MaxSum = nums[0]
        for i in range(len(nums)):
            sum += nums[i]
            if sum > MaxSum:
                MaxSum = sum
            if sum < 0:
                sum = 0
        return MaxSum

参考文献

https://blog.csdn.net/qq_34364995/article/details/80284270
https://www.cnblogs.com/patatoforsyj/p/9463945.html
https://www.cnblogs.com/linshuhui/p/9680146.html
https://blog.csdn.net/allenchenhh133/article/details/80291252
https://blog.csdn.net/rock_joker/article/details/68928150

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值