JAVA代码编写
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
教程:https://programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html
方法一:动态规划
思路:能用回溯做,但是不会
- 步骤
- 定义$dp[i] $数组:表示数字i拆分后的最大乘积
- 递推公式:
dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
- dp[i] :这里需要比较dp[i]是因为n比较大的时候,内循环会多次遍历,原本还在想dp[i]取最大了干嘛
- max((i - j) * j, dp[i - j] * j)
- (i - j) * j:不太理解,代码中j是指k个和中的一个,通过
j <= i-j
控制j,这样这里就比较好理解,这里是局部的积,算的dp[i],这里的i可能是小于n的 - dp[i - j] * j:这个很好看懂,i-j+j=i,就是算的dp[i]
- (i - j) * j:不太理解,代码中j是指k个和中的一个,通过
- dp数组初始化: dp[2] =1
- 确定遍历顺序:根据递推公式,从前往后
- 举例推导dp数组,以
n=6
举例
复杂度分析:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度:O(n)
class Solution {public int integerBreak(int n) {//dp[i] 为正整数 i 拆分后的结果的最大乘积int[] dp = new int[n+1];dp[2] = 1;for(int i = 3; i <= n; i++) {for(int j = 1; j <= i-j; j++) {// 这里的 j 其实最大值为 i-j,再大只不过是重复而已,//并且,在本题中,我们分析 dp[0], dp[1]都是无意义的,//j 最大到 i-j,就不会用到 dp[0]与dp[1]dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));// j * (i - j) 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘//而j * dp[i - j]是将 i 拆分成两个以及两个以上的个数,再相乘。}}return dp[n];}public static void main(String[] args) {Solution solution = new Solution();System.out.println(solution.integerBreak(6));}
}
方法二:回溯
思路:
复杂度分析:
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度:O(n)
class Solution {private int maxProduct = 0;public int integerBreak(int n) {if (n == 2) {return 1;}if (n == 3){return 2;}backtrack(n, 1, 1);return maxProduct;}private void backtrack(int n, int start, int product) {if (n == 0) {maxProduct = Math.max(maxProduct, product);return;}for (int i = start; i <= n; i++) {backtrack(n - i, i, product * i);}}}
96.不同的二叉搜索树
-
给你一个整数
n
,求恰由n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。示例 1:
输入:n = 3 输出:5
示例 2:
输入:n = 1 输出:1
提示:
1 <= n <= 19
教程:https://programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html
方法一:动态规划
思路:
步骤
- 定义$dp[n] $数组:由
n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有dp[n]种 - 递推公式:dp[i] += dp[j - 1] * dp[i - j];j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量
- dp数组初始化:dp[0] =1.空节点也是一棵二叉树,也是一棵二叉搜索树,这是可以说得通的。
- 确定遍历顺序:根据递推公式,从递归公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为i的状态是依靠 i之前节点数的状态。
- 举例推导dp数组,以
n=4
举例
复杂度分析:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度:O(n)
class Solution {public int numTrees(int n) {//初始化 dp 数组int[] dp = new int[n + 1];//初始化0个节点和1个节点的情况dp[0] = 1;dp[1] = 1;for (int i = 2; i <= n; i++) {for (int j = 1; j <= i; j++) {//对于第i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加//一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-jdp[i] += dp[j - 1] * dp[i - j];}}return dp[n];}
}