二叉树第二期

二叉树的关键思路:把题目的要求细化,搞清楚根节点应该做什么,然后剩下的事情抛给前、中、后序遍历框架即可。

01 构造最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。

  1. 输入:nums = [3,2,1,6,0,5]
  2. 输出:[6,3,5,null,2,0,null,null,1]
  3. 解释:递归调用如下所示:
  4. - [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5]
  5. - [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1]
  6. - 空数组,无子节点。
  7. - [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1]
  8. - 空数组,无子节点。
  9. - 只有一个元素,所以子节点是一个值为 1 的节点。
  10. - [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 []
  11. - 只有一个元素,所以子节点是一个值为 0 的节点。
  12. - 空数组,无子节点。
  13. 来源:力扣(LeetCode
  14. 链接:https://leetcode-cn.com/problems/maximum-binary-tree
  15. 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

函数签名如下:

TreeNode* constructMaximumBinaryTree(vector<int>& nums);

先明确根节点需要做什么?对于构造二叉树的问题,根节点要做的就是把自己构造出来。

需要遍历数组把最大值 maxVal 找出来,把根节点 root 做出来,然后堆 maxVal 左边数组和右边数组进行递归调用,作为 root 的左右子树。

比如,输入数组 [3, 2, 1, 6, 0, 5],对于整棵树的根节点来说,其实在做这件事:

TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
    if (nums is empty)
        return nullptr;
    // 找到数组中的最大值
    int maxVal = nums[0];
    int index = 0;
    for (int i = 0; i < nums.size(); ++i) {
        if (nums[i] > maxVal) {
            maxVal = nms[i];
            index = i;
        }
    }

    TreeNode* root = new TreeNode(maxVal);
    // 递归调用构造左右子树
    root->left = constructMaximumBinary(nums[0..index - 1]);
    root->left = constructMaximumBinary(nums[index + 1..nums.size() - 1]);

    return root;
}

对于每个节点,只需要找到当前 nums 中的最大值和对应的索引,然后递归调用左右数组构造左右子树即可。

构建一个辅助函数 build,来控制 nums 的索引:

// 将 nums[low..high] 构造成符合条件的树,返回跟节点
TreeNode* build(vector<int>& nums, int low, int high) {
    if (low > high) {
        return nullptr;
    }
    // 找到数组中的最大值和对应的索引
    int index = low;
    int maxVal = nums[index];
    for (int i = low; i <= high; ++i) {
        if (maxVal < nums[i]) {
            index = i;
            maxVal = nums[i];
        }
    }

    TreeNode* root = new TreeNoed(maxVal);

    root->left = build(nums, low, index - 1);
    root->right = build(nums, index + 1, high);

    return root;
}

TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
    return build(nums, 0, nums.size() - 1);
}

// 或者
class Solution {
public:
    int getMaxIndex(vector<int>& nums) {
        int index = 0;
        int maxV = nums[index];
        for (int i = 1; i < nums.size(); ++i) {
            if (maxV < nums[i]) {
                maxV = nums[i];
                index = i;
            }
        }
        return index;
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        if (nums.empty())
            return nullptr;

        int maxIndex = getMaxIndex(nums);
        int maxV = nums[maxIndex];
        TreeNode* root = new TreeNode(maxV);
        if (nums.size() == 1) {
            return root;
        }

        // 注意 index 范围大小
        if (maxIndex > 0) {
            vector<int> numsLeft(nums.begin(), nums.begin() + maxIndex);
            root->left = constructMaximumBinaryTree(numsLeft);
        }

        if (maxIndex < nums.size()) {
            vector<int> numsRight(nums.begin() + maxIndex + 1, nums.end());
            root->right = constructMaximumBinaryTree(numsRight);
        }


        return root;
    }
};

02 通过前序和中序遍历结果构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]


提示:

1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码如下:

// 直接操作索引:注意索引变化。左闭右闭
class Solution {
public:
    TreeNode* build(vector<int>& preorder, int preStart, int preEnd, vector<int>& inorder, int inStart, int inEnd) {
        if (preStart > preEnd) {
            return nullptr;
        }

        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootVal = preorder[preStart];
        int index = 0;
        for (int i = inStart; i <= inEnd; ++i) {
            if (inorder[i] == rootVal) {
                index = i;
                rootVal = inorder[i];
            }
        }

        TreeNode* root = new TreeNode(rootVal);
        root->left = build(preorder, preStart + 1, preStart + index - inStart, inorder, inStart, index - 1);
        root->right = build(preorder, preStart + index - inStart + 1, preEnd, inorder, index + 1, inEnd);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return build(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
    }
};

// 新建数组:注意数组长度,如果为 1,直接返回。左闭右开
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (preorder.empty())
            return nullptr;

        int rootVal = preorder[0];
        TreeNode* root = new TreeNode(rootVal);
        if (preorder.size() == 1) {
            return root;
        }

        int delimiterIndex = 0;
        for (int i = 0; i < inorder.size(); ++i) {
            if (inorder[i] == rootVal) {
                delimiterIndex = i;
                break;
            }
        }

        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());

        vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + leftInorder.size() + 1);
        vector<int> rightPreorder(preorder.begin() + leftInorder.size() + 1, preorder.end());

        root->left = buildTree(leftPreorder, leftInorder);
        root->right = buildTree(rightPreorder, rightInorder);

        return root;
    }
};