开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情
LeetCode1130:叶值的最小代价生成树
给你一个正整数数组 arr,考虑所有满足以下条件的二叉树:
- 每个节点都有 0 个或是 2 个子节点。
- 数组
arr中的值与树的中序遍历中每个叶节点的值一一对应。(知识回顾:如果一个节点有 0 个子节点,那么该节点为叶节点。) - 每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。
在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个 32 位整数。
示例:
输入: arr = [6,2,4]
输出: 32
解释:
有两种可能的树,第一种的非叶节点的总和为 36,第二种非叶节点的总和为 32。
24 24
/ \ / \
12 4 6 8
/ \ / \
6 2 2 4
提示:
2 <= arr.length <= 401 <= arr[i] <= 15- 答案保证是一个 32 位带符号整数,即小于 。
思路分析
- 回忆中序序列,对于序列中的某一个元素,它左边的全部元素在这个元素的左子树中,它右边的全部元素在这个元素的右子树中。
- 根据题意,给出的中序序列的所有元素都是叶节点。我们可以隐含地将序列分为左右两边,左半部分是左子树,右半部分是右子树。
- 我们可以递归地求解最小和,即定义一个方法getMinSum,让它返回可以得到的最小和。因为我们将序列分为左右两边,它们分别是左右子树,所以只要在两边分别找出最大元素就可以计算当前节点的值。
- 为了避免重复计算,设置备忘录记录一定范围内序列可能得到的最小和。
算法代码
public int mctFromLeafValues(int[] arr) {
if (arr == null || arr.length == 0)
return 0;
int n = arr.length;
memoization = new int[n][n];
return getMinSum(arr, 0, arr.length - 1);
}
private int[][] memoization;
private int getMinSum(int[] arr, int left, int right) {
if (left == right) {
return 0;
}
if (memoization[left][right] != 0)
return memoization[left][right];
int minSum = Integer.MAX_VALUE;
for (int i = left; i < right; i++) {
int leftStart = left, leftEnd = i, rightStart = i + 1, rightEnd = right;
int temp = getMinSum(arr, leftStart, leftEnd) + getMinSum(arr, rightStart, rightEnd) +
getMaxNum(arr, leftStart, leftEnd) * getMaxNum(arr, rightStart, rightEnd);
minSum = Math.min(temp, minSum);
}
memoization[left][right] = minSum;
return minSum;
}
private int getMaxNum(int[] arr, int left, int right) {
int max = Integer.MIN_VALUE;
for (int i = left; i <= right; i++) {
max = Math.max(max, arr[i]);
}
return max;
}
结果详情
算法复杂度
- 空间复杂度:
- 时间复杂度:
在掘金(JUEJIN)一起进步,一起成长!