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

day19是休息日,到时候我会补一篇关于二叉树的总结。
所以今天是day20.

目录

  • 654.最大二叉树
    • 思路
    • 解题方法
    • 复杂度
    • Code
  • 617.合并二叉树
    • 思路
    • 解题方法
    • 复杂度
    • Code
  • 700.二叉搜索树中的搜索
    • 思路
    • 解题方法
    • 复杂度
    • Code
  • 98.验证二叉搜索树
    • 思路
    • 解题方法
    • 复杂度
    • Code
  • 总结

654.最大二叉树

链接: 最大二叉树

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

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

思路

每个二叉树节点都可以认为是一棵子树的根节点,对于根节点,首先要做的当然是把想办法把自己先构造出来,然后想办法构造自己的左右子树。

所以,我们要遍历数组把找到最大值 maxVal,从而把根节点 root 做出来,然后对 maxVal 左边的数组和右边的数组进行递归构建,作为 root 的左右子树。

解题方法

还是递归

复杂度

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n)

Code

class Solution {
public:
    /* 主函数 */
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return build(nums, 0, nums.size() - 1);
    }

    /* 定义:将 nums[lo..hi] 构造成符合条件的树,返回根节点 */
    TreeNode* build(vector<int>& nums, int lo, int hi) {
        /* base case */
        if (lo > hi) {
            return nullptr;
        }

        /* 找到数组中的最大值和对应的索引 */
        int index = -1, maxVal = INT_MIN;
        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;
    }
};

617.合并二叉树

链接: 合并二叉树

思路

前序遍历

解题方法

前序遍历后序遍历都可以,前序最简单

复杂度

  • 时间复杂度:O(logn)
  • 空间复杂度:O(n)

Code

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
        if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
        // 修改了t1的数值和结构
        t1->val += t2->val;                             // 中
        t1->left = mergeTrees(t1->left, t2->left);      // 左
        t1->right = mergeTrees(t1->right, t2->right);   // 右
        return t1;
    }
};

700.二叉搜索树中的搜索

链接: 二叉搜索树中的搜索

思路

利用 BST 左小右大的特性,可以避免搜索整棵二叉树去寻找元素,从而提升效率。

解题方法

使用BST,也是递归逻辑。

复杂度

  • 时间复杂度:O(logn)
  • 空间复杂度:O(logn)

Code

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int target) {
        if (root == NULL) {
            return NULL;
        }
        // 去左子树搜索
        if (root->val > target) {
            return searchBST(root->left, target);
        }
        // 去右子树搜索
        if (root->val < target) {
            return searchBST(root->right, target);
        }
        return root;
    }
};

98.验证二叉搜索树

链接: 验证二叉搜索树

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

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

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

思路

还是使用递归
我做这题很容易有误区:BST 不是左小右大么,那我只要检查 root.val > root.left.val 且 root.val < root.right.val 不就行了?

这样是不对的,因为 BST 左小右大的特性是指 root.val 要比左子树的所有节点都更大,要比右子树的所有节点都小,你只检查左右两个子节点当然是不够的。

解题方法

正确解法是通过使用辅助函数,增加函数参数列表,在参数中携带额外信息,将这种约束传递给子树的所有节点,这也是二叉搜索树算法的一个小技巧

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

Code

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        // 限定以root为根的子树节点必须满足max.val > root.val > min.val 
        return checkValidBST(root, nullptr, nullptr);
    }

    // 限定root节点符合min和max的限制
    bool checkValidBST(TreeNode* root, TreeNode* minNode, TreeNode* maxNode) {
        // base case
        if (root == nullptr) return true;
        // 若root.val不符合max和min的限制,说明不是合法BST
        if (minNode != nullptr && root->val <= minNode->val) return false;
        if (maxNode != nullptr && root->val >= maxNode->val) return false;
        // 限定左子树的最大值是root.val,右子树的最小值是root.val
        return checkValidBST(root->left, minNode, root)
            && checkValidBST(root->right, root, maxNode);
    }
};

总结

  • 第一题又是构造二叉树,昨天大家刚刚做完 中序后序确定二叉树,今天做这个 应该会容易一些, 先看视频,好好体会一下 为什么构造二叉树都是 前序遍历
  • 第二题是一起操作两个二叉树了, 估计大家也没一起操作过两个二叉树,也不知道该如何一起操作,我得先看一遍视频。
  • 第三题比较简单,了解二叉搜索树的特性就能解决
  • 第四题遇到 搜索树,一定想着中序遍历,这样才能利用上特性。
    但本题是有陷阱的
    参考文档:
  • 链接: 最大二叉树
  • 链接: 合并二叉树
  • 链接: 二叉搜索树中的搜索
  • 链接: 验证二叉搜索树

你可能感兴趣的:(算法刷题,算法)