代码随想录算法训练营第41天 | 343.整数拆分 96.不同的二叉搜索树

整数拆分

代码随想录算法训练营第41天 | 343.整数拆分 96.不同的二叉搜索树_第1张图片

  • dp数组的含义:dp[i] 表示将 i 拆分所能得到的最大乘积。
  • 递推公式:dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))。我们对 j 从1开始遍历,检验不同的拆分方式能产生的最大乘积。对于拆分方式的选择,要么拆成 j 和 i-j 两个数,要么继续拆 i - j (dp[i - j]),为什么不继续拆 j 呢?
    因为拆分 j 的情况其实在遍历的过程中已经覆盖了。也可以理解为 j * (i - j) 是拆分为两个数的情况,而 dp[i - j] * j 则是拆成两个以上数的情况,覆盖了拆出 j 时的所有拆分方式。
  • 初始化:与爬楼梯相似地,dp[0] 和 dp[1] 都没有意义,所以应该初始化 dp[2],从3开始求解。
  • 遍历顺序:从前向后,因为求解 dp[i] 需要知道 dp[i - j]。还有一个可以优化的小点,j 其实只遍历到 i/2 就可以,因为乘积大的情况一般在拆分出的那几个数字相近的时候出现。
class Solution{
public:
	int integerBreak(int n) {
		vector<int> dp(n + 1);
		dp[2] = 1;
		for(int i = 3; i <= n; i++) {
			for(int j = 1; j <= i/2; j++) {
				dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]));
			}
		}
		return dp[n];
	}
};

不同的二叉搜索树

代码随想录算法训练营第41天 | 343.整数拆分 96.不同的二叉搜索树_第2张图片
假设 dp[i] 表示有 i 个节点时能构造出的二叉搜索树的数目。
以 n=3 为例,我们在思考这一问题时可以分别以1、2、3为头节点构建二叉搜索树。
当1为头节点时,其左叶子应该为空,右叶子应该有两个节点2、3,这种情况的树的数目应该为dp[0] * dp[2]
当2为头节点时,其左叶子应该为1,右叶子应该有两个节点3,这种情况的树的数目应该为dp[1] * dp[1]
当3为头节点时,其左叶子应该为2、3,右叶子应该为空,这种情况的树的数目应该为dp[2] * dp[0]
这样我们就得到了用前面的状态表达后续状态的递推式。
对于初始化,如果只有一个节点,二叉搜索树的数目只有1,同时0个节点也是合法的,因为空树也是二叉搜索树,dp[0] = dp[1] = 1

class Solution{
public:
	int numTrees(int n) {
		vector<int> dp(n + 1, 0);
		dp[0] = 1;
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= i; j++) {  
				// 遍历选择j当做根节点,则其右叶子应该有i-j个节点,左叶子应该有j-1节点
				dp[i] += dp[i - j] * dp[j - 1];
			}
		}
		return dp[n];
	}
};

你可能感兴趣的:(算法,数据结构)