算法训练 Day 41

LeetCode 343. 整数拆分

题目链接:343. 整数拆分

思路:动规五部曲分析:

  1. 确定dp数组以及下标的含义
    dp[i]表示拆分数字i能得到的最大乘积。
  2. 确定递推公式
    dp[i]最大乘积是怎么得到的呢?
    从1开始遍历到j,有两种方式能得到dp[i]
    1) j ∗ ( i − j ) j * (i - j) j(ij)相乘直接就是最大的乘积;
    2) j ∗ d p [ i − j ] j * dp[i-j] jdp[ij],相当于再继续拆分 ( i − j ) (i - j) (ij)
    也可以这么理解, j ∗ ( i − j ) j * (i - j) j(ij)是直接把整数拆分为两个数相乘,而 j ∗ d p [ i − j ] j * dp[i - j] jdp[ij]是拆分成两个以上的数相乘,所以递推公式为: d p [ i ] = m a x ( d p [ i ] , ( i − j ) ∗ j , d p [ i − j ] ∗ j ) dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j}) dp[i]=max(dp[i],(ij)j,dp[ij]j)
  3. dp数组的初始化
    因为n>=2的,所以初始化dp[0]和dp[1]其实是没有意义的,初始化dp[2]=1即可。
  4. 确定遍历顺序
    因为dp数组从dp[2](索引3)时才开始有实际意义,所以i从索引4开始从前向后遍历到n。
  5. 打印dp数组的最后一位即为结果。

Python版本:

class Solution:
    def integerBreak(self, n: int) -> int:
        if n<=3:
            return n-1
        dp = [0] * (n+1)
        dp[2] = 1
        for i in range(3, n+1):
            for j in range(1, i//2 + 1):
                dp[i] = max(dp[i], dp[i-j]*j, j*(i-j))

        return dp[-1]

时间复杂度: O ( n ) O(n) O(n),空间复杂度: O ( n ) O(n) O(n)

go版本:

func integerBreak(n int) int {
    if n<=3 {
        return n-1
    }
    dp := make([]int, n+1)
    dp[2] = 1
    for i:=3; i<=n; i++ {
        for j:=1; j<i/2+1; j++ {
            dp[i] = max(dp[i], max(dp[i-j]*j, j*(i-j)))
        }
    }
    return dp[len(dp)-1]
}

func max(i, j int) int {
    if i>j {
        return i
    } else {
        return j
    }
}

LeetCode 96. 不同的二叉搜索树

题目链接:96. 不同的二叉搜索树

思路:动规五部曲分析:

  1. 确定dp数组以及下标的含义
    dp[i]表示 i 个节点组成且节点值从 1 到 i 互不相同的二叉搜索树有 dp[i] 种。

  2. 确定递推公式
    dp[i]是怎么得到的呢?这个问题其实比较抽象,我们如果做这种题一时没有明确的递推公式的思路的话,可以通过枚举法来找找规律。
    dp[1] = 1
    dp[2] = 2
    在这里插入图片描述

    从n=3开始,有5种二叉搜索树(图摘自代码随想录):
    在这里插入图片描述
    我们发现,以1做根节点的情况有两种,以2做根节点的情况有1种,以3做根节点的情况有两种;
    其中:以1为根节点时其右子树的二叉树结构与n=2时的两种二叉树结构相同;以2为根节点时其左、右子树的树结构与n=1时的二叉树结构相同;以3为根节点时其左子树的二叉树结构与n=2时的两种二叉树结构相同。
    综上所述,元素1为根结点的二叉搜索树数量 = 左子树有0个元素的搜索树数量 * 右子树有2个元素的搜索树数量;元素2为根结点的二叉搜索树数量 = 左子树有1个元素的搜索树数量 * 右子树有1个元素的搜索树数量;元素3为根结点的二叉搜索树数量 = 左子树有2个元素的搜索树数量 * 右子树有0个元素的搜索树数量。三种情况的数量之和就是dp[3]。
    所以, d p [ 3 ] = d p [ 0 ] ∗ d p [ 2 ] + d p [ 1 ] ∗ d p [ 1 ] + d p [ 2 ] ∗ d p [ 0 ] dp[3] = dp[0] * dp[2] + dp[1] * dp[1] + dp[2] * dp[0] dp[3]=dp[0]dp[2]+dp[1]dp[1]+dp[2]dp[0]
    如果 j 作为根节点的元素, d p [ i ] = d p [ 0 ] ∗ d p [ i − 1 ] + d p [ 1 ] ∗ d p [ i − 2 ] + . . . + d p [ j − 1 ] ∗ d p [ i − j ] + . . . + d p [ i − 2 ] ∗ d p [ 1 ] + d p [ i − 1 ] ∗ d p [ 0 ] dp[i] = dp[0] * dp[i-1] + dp[1] * dp[i-2] + ... + dp[j-1] * dp[i-j] + ...+ dp[i-2] * dp[1] + dp[i-1] * dp[0] dp[i]=dp[0]dp[i1]+dp[1]dp[i2]+...+dp[j1]dp[ij]+...+dp[i2]dp[1]+dp[i1]dp[0]
    也就是说,j 从1开始遍历到 i,dp[i] 的递推公式为: d p [ i ] + = d p [ j − 1 ] ∗ d p [ i − j ] dp[i] += dp[j-1] * dp[i-j] dp[i]+=dp[j1]dp[ij]

  3. dp数组的初始化
    本题在初始化的时候还有一个细节,那就是当n=0时 dp[0] 也是有意义的,因为空节点也是一个二叉搜索树,所以 d p [ 0 ] = 1 dp[0]=1 dp[0]=1

  4. 确定遍历顺序
    dp数组从i = 1,从前向后遍历到n。

  5. 打印dp数组的最后一位即为结果。

Python版本:

class Solution:
    def numTrees(self, n: int) -> int:
        if n<=2:
            return n
        dp = [0] * (n+1)
        dp[0] = 1
        for i in range(1, n+1):
            for j in range(1, i+1):
                dp[i] += dp[j-1]*dp[i-j]

        return dp[-1]

时间复杂度: O ( n ) O(n) O(n),空间复杂度: O ( n ) O(n) O(n)

go版本:

func numTrees(n int) int {
    if n<=2 {
        return n
    }
    dp := make([]int, n+1)
    dp[0] = 1
    for i:=1; i<=n; i++ {
        for j:=1; j<=i; j++ {
            dp[i] += dp[j-1]*dp[i-j]
        }
    }
    return dp[len(dp)-1]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值