思路篇讲了「遍历」和「分解问题」两种思维方式,本文讲二叉树的构造类问题。
二叉树的构造问题一般都是使用「分解问题」的思路:构造整棵树 = 根节点 + 构造左子树 + 构造右子树。
接下来直接看题。
先来道简单的,这是力扣第 654 题「最大二叉树」,题目如下:
函数签名如下:
TreeNode constructMaximumBinaryTree(int[] nums);
每个二叉树节点都可以认为是一棵子树的根节点,对于根节点,首先要做的当然是把想办法把自己先构造出来,然后想办法构造自己的左右子树。
所以,我们要遍历数组把找到最大值 maxVal
,从而把根节点 root
做出来,然后对 maxVal
左边的数组和右边的数组进行递归构建,作为 root
的左右子树。
按照题目给出的例子,输入的数组为 [3,2,1,6,0,5]
,对于整棵树的根节点来说,其实在做这件事:
TreeNode constructMaximumBinaryTree([3,2,1,6,0,5]) {
// 找到数组中的最大值
TreeNode root = new TreeNode(6);
// 递归调用构造左右子树
root.left = constructMaximumBinaryTree([3,2,1]);
root.right = constructMaximumBinaryTree([0,5]);
return root;
}
再详细一点,就是如下伪码:
TreeNode constructMaximumBinaryTree(int[] nums) {
if (nums is empty) return null;
// 找到数组中的最大值
int maxVal = Integer.MIN_VALUE;
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > maxVal) {
maxVal = nums[i];
index = i;
}
}
TreeNode root = new TreeNode(maxVal);
// 递归调用构造左右子树
root.left = constructMaximumBinaryTree(nums[0..index-1]);
root.right = constructMaximumBinaryTree(nums[index+1..nums.length-1]);
return root;
}
当前 nums
中的最大值就是根节点,然后根据索引递归调用左右数组构造左右子树即可。
明确了思路,我们可以重新写一个辅助函数 build
,来控制 nums
的索引:
/* 主函数 */
TreeNode constructMaximumBinaryTree(int[] nums) {
return build(nums, 0, nums.length - 1);
}
// 定义:将 nums[lo..hi] 构造成符合条件的树,返回根节点
TreeNode build(int[] nums, int lo, int hi) {
// base case
if (lo > hi) {
return null;
}
// 找到数组中的最大值和对应的索引
int index = -1, maxVal = Integer.MIN_VALUE;
for (int i = lo; i <= hi; i++) {
if (maxVal < nums[i]) {
index = i;
maxVal = nums[i];
}
}
// 先构造出根节点
TreeNode root = new TreeNode(maxVal);
// 递归调用构造左右子树
root.left = build(nums, lo, index - 1);
root.right = build(nums, index + 1, hi);
return root;
}
奉上作者的可视化: