随想录Day40|343. 整数拆分 ● 96.不同的二叉搜索树

343. 整数拆分

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积 。

示例 1:

输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

提示:

  • 2 <= n <= 58

思路:

本题可以采用两种方法,一种是用数学方法推导出公式,利用到了e来辅助公式推导,感兴趣的可以看一下下面的链接:

343. 整数拆分 - 力扣(LeetCode)

复杂度分析:
时间复杂度 O(1) : 仅有求整、求余、次方运算。
求整和求余运算:查阅资料,提到不超过机器数的整数可以看作是 O(1)O(1)O(1) ;
幂运算:查阅资料,提到浮点取幂为 O(1)O(1)O(1) 。
空间复杂度 O(1): a 和 b 使用常数大小额外空间。

第二种就是动态规划的思想了,核心思想就是把拆数字分成两种情况

1.拆成了2个数字

2.拆成了2个以上的数字

为什么分成这两种情况呢?首先明确下dp数组的定义

dp[i]表示i这个数可以拆成的数的乘积的最大值

当我们利用dp数组中的数推导出dp[i]时,实际上可以把dp[j] 当成是多个数乘积的最优解,其实也是多个数组合起来的

就例如

dp[4] = 2*2 = 4

dp[7] = 3*dp[7-3] = 3*dp[4] = 3*2*2 = 12;

所以其中的dp[4]可以看成很多个数的乘积,这当然也包括在7能拆分的数里面了

而仅有两个数组合成的例子也就是dp[4] = 2*2 = 4了

前面的dp[0] = 0,dp[1] = 0,dp[2] = 1,dp[3] = 2,所以dp[4]的组成元素是不包含前面dp数组的元素在内

这是因为dp中的元素都是拆分过的,所以必定是两个数以上的乘积,所以面对这种仅有两个数组成的元素就用不上了

故最后的递推公式为

dp[i] = Math.max(Math.max((i - j) * j, dp[i - j] * j),dp[i]),其中j从0遍历到i/2

代码如下

class Solution {
    public static int integerBreak(int n) {
        //1.dp数组含义:dp[i]表示i这个数可以拆成的数的乘积的最大值
        //2.递推公式:dp[i] = Math.max(Math.max((i - j) * j, dp[i - j] * j),dp[i]),其中j从0遍历到i/2,
        //因为遍历到i/2往后的情况其实已经算进去了
        //由于我们给dp数组设定的含义,所以dp[i-j]可以看做自己很多个被拆数的乘积最大值
        //所以本质上也是拆了很多个数,但是不显示在计算中
        //但是两个数都没有拆分的情况并不在里面,所以要额外添加(i - j) * j作为对比
        //3.初始化dp数组:由于0,1是拆不了的,没有意义,只要让他们不影响结果就可以
        //设置为0吧
        //4.遍历顺序应该是从前到后,因为后面的数需要前面数的推导而来
        //5.打印dp
        int[]dp = new int[n+1];
        dp[0] = 0;
        dp[1] =0;
        for(int i=2;i<=n;i++){
            //搜寻最大值
            int max = -1;
            for(int j =1;j<=i/2;j++){
                //算出的值与自身对比
                //这里Math.max((i - j) * j, dp[i - j] * j)是把数分成了两种情况
                //1.只有两个数的乘积组成的最大值
                //2.有多个数的乘积组成的最大值
                //dp[i - j] * j就属于是多个数的情况
                //因为dp数组的值都算是已经拆分好的
                max = Math.max(Math.max((i - j) * j, dp[i - j] * j),max);
            }
            dp[i] = max;
        }
        /*//打印
        for(int i =0;i<=n;i++){
            System.out.print(i+"\t");
        }
        System.out.println();
        for(int i:dp){
            System.out.print(i+"\t");
        }
        System.out.println();*/
        //结果
        return dp[n];
    }
}

96. 不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例 1:

输入:n = 3
输出:5

示例 2:

输入:n = 1
输出:1

提示:

  • 1 <= n <= 19

思路:

先明确dp数组含义为有i个节点时,二叉搜索树可能的组合有dp[i]种

这题的核心思想就是遍历从1-i的所有节点,使之成为头节点,然后计算他们的和最后得到dp[i]

递推公式为dp[i] = 所有的节点为头结点的可能性之和

那么每个节点为头结点时,有多少种组合要怎么求呢?

可以先算出左右子树节点的数量

根据二叉搜索树的性质可以得出

左子树的节点数为: j-1

右子树的节点数为: i-j

不太清楚的小伙伴可以去复习一下二叉搜索树的性质

然后看一下我们dp数组的含义,是不是左右子树可能的组合都能得到了?

左子树可能的组合为dp[j-1]

右子树可能的组合为dp[i-j]

所有的组合就是dp[j-1]*dp[i-j]

这就得出以j为节点时的组合数量了,接下来只要累加到i即可

代码如下:

class Solution {
    public static int numTrees(int n) {
        //思路:
        //1.dp[i]代表有i个节点时二叉搜索树可能的数量
        //2.递推公式为dp[i] = 所有的节点为头结点的可能性之和
        //3.初始化dp[0] =1;因为空节点也是二叉搜索树
        //4.从前向后推导
        //5.打印dp

        //1.
        int[] dp = new int[n+1];
        Arrays.fill(dp,0);//开始全设为0
        dp[0] = 1;
        for(int i =1;i<=n;i++){
            for(int j =1;j<=i;j++){
                //枚举j为头结点的情况相加
                //左边的可能乘右边的可能,得出可能的组合数量
                dp[i] += dp[j-1]*dp[i-j];
            }
        }
        //打印
        /*for(int i =0;i<=n;i++){
            System.out.print(i+"\t");
        }
        System.out.println();
        for(int i =0;i<=n;i++){
            System.out.print(dp[i]+"\t");
        }
        System.out.println();*/
        return dp[n];
    }
}

感谢观看!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值