代码随想录算法训练营Day 20|654. 最大二叉树、617. 合并二叉树、617. 合并二叉树、98. 验证二叉搜索树

75 阅读5分钟

654. 最大二叉树

题目链接

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

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

返回 nums 构建的 最大二叉树

思路

构造树一般采用前序遍历,先构造中间节点,然后递归构造左子树和右子树。

  1. 确定递归函数的参数和返回值 参数是存放元素的数组,返回该数组构造的二叉树的头节点。
  2. 确定终止条件 当递归遍历时,如果传入的数组大小为1,则说明遍历到了叶子节点,此时应该返回一个新的节点,节点的值为nums[0]
  3. 确定单层遍历逻辑 首先要找到数组中最大值和其对应的下标,最大值构建根节点,下标用来切割数组。 最大值的下标的左区间,构造左子树,这里需要判断maxIndex > 0, 因为传入数组至少有一个值; 最大值的下标的右区间,构造右子树,这里需要判断maxIndex < nums.length -1, 因为传入数组至少有一个值;
var constructMaximumBinaryTree = function(nums) {
    if(nums.length == 1){
        return new TreeNode(nums[0])
    }
    let maxValue = 0
    let maxIndex = 0
    for(let i=0; i<nums.length; i++){
        if(nums[i]>maxValue){
            maxValue = nums[i]
            maxIndex = i
        }
    }
    let node = new TreeNode(maxValue)
    //每次递归调用时都构造了一个新数组,这是比较耗时的
    if(maxIndex > 0){
        node.left = constructMaximumBinaryTree(nums.slice(0, maxIndex))
    }
    if(maxIndex<nums.length-1){
        node.right = constructMaximumBinaryTree(nums.slice(maxIndex+1))
    }
    return node
};

优化

var constructMaximumBinaryTree = function(nums) {
    function traversal(arr, left, right){
        if(left>right){
            return null
        }
        let maxValue = 0
        let maxIndex = 0
        for(let i=left; i<=right; i++){
            if(arr[i]>=maxValue){
                maxValue = arr[i]
                maxIndex = i
            }
        }
        console.log(maxValue, maxIndex)
        let root = new TreeNode(maxValue)
        root.left = traversal(arr, left, maxIndex-1)
        root.right = traversal(arr, maxIndex+1, right)
        return root
    }
    return traversal(nums, 0, nums.length-1)
};

617. 合并二叉树

题目链接

要求:给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意:  合并过程必须从两个树的根节点开始。

思路

本题难点主要是如何同时遍历两个二叉树,其实就是跟遍历一个二叉树类似,只不过是传入两个节点

递归法:采用哪种遍历都是可以的,这里我使用前序遍历

var mergeTrees = function(root1, root2) {
    //同时处理两个二叉树,采用前序遍历,可以直接在tree1上改也可以新建一个二叉树
    //递归
    if(root1 == null) return root2
    if(root2 == null) return root1
    root1.val += root2.val
    root1.left = mergeTrees(root1.left, root2.left)
    root1.right = mergeTrees(root1.right, root2.right)
    return root1
};

700. 二叉搜索树中的搜索

题目链接

要求:给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意:  合并过程必须从两个树的根节点开始。

思路

1.递归法

var searchBST = function(root, val) {
    //利用搜索树的特性,左节点比根节点小,右节点比根节点大
    if(root == null || root.val == val) return root
    //因为函数有返回值 所以需要去接收 或者直接返回
    if(root.val > val) return searchBST(root.left, val)
    if(root.val < val) return searchBST(root.right, val)
    return null
};

2.迭代法 一般二叉树的迭代法,用栈模拟深度遍历,用队列模拟广度遍历,搜索二叉树中不需要搜索其他点,中间节点 > val,则往左搜索,< val 则往右搜索。

var searchBST = function(root, val) {
    while(root != null){
        if(root.val > val) root = root.left
        else if(root.val < val) root = root.right
        else return root
    }
    return null
};

98. 验证二叉搜索树

题目链接

要求:给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

思路

本题不能直接比较左节点小于中间节点,右节点大于中间节点,因为可能会出现这样的情况,

image.png

我们需要的是左子树的所有节点小于中间节点,右子树所有节点大于中间节点。 1.递归

var isValidBST = function(root) {
    let maxVal = -Infinity //记录遍历的前一个节点值
    function isValid(root){
         //中序遍历
        if(root == null) return true
        let left = isValid(root.left)
        if(maxVal < root.val) {
            maxVal = root.val
        }else {
            return false
        }
        let right = isValid(root.right)
        return left && right
    }
    return isValid(root)
};

var isValidBST = function(root) {
    let pre = null //记录前一个节点
    function isValid(root){
         //中序遍历
        if(root == null) return true
        let left = isValid(root.left)
        if(pre != null && pre.val >= root.val) {
            return false
        }
        pre = root
        let right = isValid(root.right)
        return left && right
    }
    return isValid(root)
};

2.迭代

var isValidBST = function(root) {
    let stack = []
    let cur = root
    let pre = null
    while(cur != null || stack.length != 0){
        if(cur != null){
            stack.push(cur)
            cur = cur.left
        }else {
            cur = stack.pop()
            if(pre != null && pre.val >= cur.val){
                return false
            }
            pre = cur
            cur = cur.right
        }
    }
    return true
};