代码随想录算法训练营第三期day20-二叉树06

目录

1、T654:最大二叉树

1.1 暴力法

1.2 优化版

1.3 简.优化版

2、T617:合并二叉树

2.1 法1、递归

2.1.1 前序

2.1.2 中序

2.1.3 后序

2.2 法2、迭代

3、T700:二叉搜索树中的搜索

3.1 法1、递归

3.2 法2、迭代法

4、T98:验证二叉搜索树

4.1 法1、递归

4.1.1 版本Ⅰ、讲解版

4.1.2 版本Ⅱ、过渡版

4.1.3 最终版

4.2 法2、迭代

4.2.1 普通迭代

4.2.2 统一迭代


1、T654:最大二叉树

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

  1. 创建一个根节点,其值为 nums 中的最大值。

  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。

  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 。

提示:

  • 1 <= nums.length <= 1000

  • 0 <= nums[i] <= 1000

  • nums 中的所有整数 互不相同

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

1.1 暴力法

一上来直接暴力解法:

    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        if (nums.size() == 0) return nullptr;//🚩题目中是保证了>=1,但如果不在下面设置几个if就不能没有这行
        int maxVal = INT_MIN, maxValIndex;
        for (int i = 0; i < nums.size(); ++i) {
            if (maxVal < nums[i]) {
                maxVal = nums[i];
                maxValIndex = i;
            }
        }
        TreeNode* root = new TreeNode(maxVal);
        vector<int> leftNums(nums.begin(), nums.begin() + maxValIndex);
        vector<int> rightNums(nums.begin() + maxValIndex + 1, nums.end());
        root->left = constructMaximumBinaryTree(leftNums);
        root->right = constructMaximumBinaryTree(rightNums);
        // root->right = constructMaximumBinaryTree(nums.assign(nums.begin() + maxValIndex + 1, nums.end()));
        //error(还不怎么会分割vector)
        return root;
    }

其实已经差不多了,就是还有几处要补充一下,补充之后得到版本一:

    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        TreeNode* root = new TreeNode(0);
        if (nums.size() == 1) {
            root->val = nums[0];
            return root;
        }
        int maxVal = INT_MIN, maxValIndex;
        for (int i = 0; i < nums.size(); ++i) {
            if (maxVal < nums[i]) {
                maxVal = nums[i];
                maxValIndex = i;
            }
        }
        root->val = maxVal;
        // 最大值所在的下标左区间 构造左子树
        if (maxValIndex > 0) {//🚩保证左区间至少有一个数值(为了不让空节点进入递归,没有就无法通过)
            vector<int> leftNums(nums.begin(), nums.begin() + maxValIndex);
            root->left = constructMaximumBinaryTree(leftNums);
        }
        if (maxValIndex < nums.size() - 1) {//保证右区间至少有一个数值
            vector<int> rightNums(nums.begin() + maxValIndex + 1, nums.end());
            root->right = constructMaximumBinaryTree(rightNums);
        }
        return root;
    }

上面两种写法,🚩标记的地方必须至少有一个,否则递归会导致栈溢出(无终止或者不允许空值进栈) 

1.2 优化版

优化的思路和前面构造二叉树一样,在原有数组的基础上,通过索引递归更新分割数组

    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return constructMaximumBinaryTree(nums, 0, nums.size());
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums, int left, int right) {
        if (left == right) return nullptr;//没有这行就会栈溢出
        TreeNode* root = new TreeNode(0);
        // if (nums.size() == 1) {//这一行也可以
        if (right - left == 1) {
            root->val = nums[left];
            return root;
        }
        int maxVal = INT_MIN, maxValIndex;
        for (int i = left; i < right; ++i) {
            if (maxVal < nums[i]) {
                maxVal = nums[i];
                maxValIndex = i;
            }
        }
        root->val = maxVal;
        root->left = constructMaximumBinaryTree(nums, left, maxValIndex);
        root->right = constructMaximumBinaryTree(nums, maxValIndex + 1, right);
        return root;
    }

1.3 简.优化版

还可以再简化,把 right - left == 1 的专用逻辑省略,省去maxVal这一变量

C++:

    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return constructMaximumBinaryTree(nums, 0, nums.size());
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums, int left, int right) {
        if (left >= right) return nullptr;//没有这行就会栈溢出
        // TreeNode* root = new TreeNode(0);

        // if (right - left == 1) {//并入了下面的for循环
        //     root->val = nums[left];
        //     return root;
        // }
        int maxValIndex = left;
        for (int i = left + 1; i < right; ++i) {
            if (nums[maxValIndex] < nums[i]) {
                maxValIndex = i;
            }
        }
        TreeNode* root = new TreeNode(nums[maxValIndex]);
        root->left = constructMaximumBinaryTree(nums, left, maxValIndex);
        root->right = constructMaximumBinaryTree(nums, maxValIndex + 1, right);
        return root;
    } 

Java:

    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return constructMaximumBinaryTree(nums, 0, nums.length);
    }
    private TreeNode constructMaximumBinaryTree(int[] nums, int left, int right) {
        if (left >= right) return null;
        int maxValIndex = left;
        for (int i = left + 1; i < right; ++i) {
            if (nums[maxValIndex] < nums[i]) {
                maxValIndex = i;
            }
        }
        TreeNode root = new TreeNode(nums[maxValIndex]);
        root.left = constructMaximumBinaryTree(nums, left, maxValIndex);
        root.right = constructMaximumBinaryTree(nums, maxValIndex + 1, right);
        return root;
    }

可以发现上面的代码要简洁一些,主要是因为优化版允许空节点进入递归,所以不用在递归的时候加判断节点是否为空

2、T617:合并二叉树

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

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

返回合并后的二叉树。

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

提示:

  • 两棵树中的节点数目在范围 [0, 2000] 内

  • -104 <= Node.val <= 104

S:

2.1 法1、递归

2.1.1 前序

C++:

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (!root1) return root2;//都为空也没关系
        if (!root2) return root1;
        TreeNode* root = new TreeNode(root1->val + root2->val);
        root->left = mergeTrees(root1->left, root2->left);
        root->right = mergeTrees(root1->right, root2->right);
        return root;
    }

->免于new一个新TreeNode:

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (!root1) return root2;//都为空也没关系
        if (!root2) return root1;
        root1->val += root2->val;
        root1->left = mergeTrees(root1->left, root2->left);
        root1->right = mergeTrees(root1->right, root2->right);
        return root1;
    }

Java:

    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        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;
    }

2.1.2 中序

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (!root1) return root2;//都为空也没关系
        if (!root2) return root1;
        root1->left = mergeTrees(root1->left, root2->left);
        root1->val += root2->val;
        root1->right = mergeTrees(root1->right, root2->right);
        return root1;
    }

2.1.3 后序

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (!root1) return root2;//都为空也没关系
        if (!root2) return root1;
        root1->left = mergeTrees(root1->left, root2->left);
        root1->right = mergeTrees(root1->right, root2->right);
        root1->val += root2->val;
        return root1;
    }

2.2 法2、迭代

C++:

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (!root1) return root2;
        if (!root2) return root1;
        queue<TreeNode*> que;
        que.push(root1);
        que.push(root2);
        while (!que.empty()) {
            TreeNode* node1 = que.front(); que.pop();
            TreeNode* node2 = que.front(); que.pop();
            node1->val += node2->val;

            if (node1->left && node2->left) {
                que.push(node1->left);
                que.push(node2->left);
            }
            if (node1->right && node2->right) {
                que.push(node1->right);
                que.push(node2->right);
            }
            if (!node1->left && node2->left) {
                node1->left = node2->left;
            }
            if (!node1->right && node2->right) {
                node1->right = node2->right;
            }
            // if (node1->right && !node2->right) {
            //     node2->right = node1->right;
            // }
        }
        return root1;
    }

Java:

    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if (root1 == null) return root2;
        if (root2 == null) return root1;
        Deque<TreeNode> queue = new LinkedList<>();
        queue.addLast(root1);
        queue.addLast(root2);
        while (!queue.isEmpty()) {
            TreeNode node1 = queue.poll();
            TreeNode node2 = queue.poll();
            node1.val += node2.val;
            if (node1.left != null && node2.left != null) {
                queue.add(node1.left);
                queue.add(node2.left);
            }
            if (node1.right != null && node2.right != null) {
                queue.add(node1.right);
                queue.add(node2.right);
            }
            if (node1.left == null && node2.left != null) {
                node1.left = node2.left;
            }
            if (node1.right == null && node2.right != null) {
                node1.right = node2.right;
            }
        }
        return root1;
    }

3、T700:二叉搜索树中的搜索

T700:给定二叉搜索树(BST)的根节点 root 和一个整数值 val。

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

提示:

  • 数中节点数在 [1, 5000] 范围内

  • 1 <= Node.val <= 107

  • root 是二叉搜索树

  • 1 <= val <= 107

S:

3.1 法1、递归

C++:

    TreeNode* searchBST(TreeNode* root, int val) {
        if (!root || root->val == val) return root;
        if (root->val < val) return searchBST(root->right, val);
        if (root->val > val) return searchBST(root->left, val);
        return nullptr;
    }

Java:

    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null || root.val == val) return root;
        if (root.val < val) return searchBST(root.right, val);
        if (root.val > val) return searchBST(root.left, val);
        return null;
    }

3.2 法2、迭代法

C++:

    TreeNode* searchBST(TreeNode* root, int val) {
        while (root) {
            if (root->val < val) root = root->right;
            else if (root->val > val) root = root->left;
            else return root;
        }
        return nullptr;
    }

Java:

    public TreeNode searchBST(TreeNode root, int val) {
        while (root != null) {
            if (root.val > val) root = root.left;
            else if (root.val < val) root = root.right;//没有else->空指针异常
            else return root;
        }
        return null;
    }

4、T98:验证二叉搜索树

T98:给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。
S:不论是前中后哪种遍历顺序,左都在右的前面,因此只能选择从小到大比,故选中序遍历

4.1 法1、递归

4.1.1 版本Ⅰ、讲解版

一句话,递归中序遍历将二叉搜索树转变成一个数组再逐个比大小

C++:

    vector<int> vec;
    bool isValidBST(TreeNode* root) {
        if (!root) return true;
        traversal(root);
        // for (int i = 0; i < vec.size() - 1; ++i) {//二选一
        //     if (vec[i] >= vec[i + 1]) return false;
        // }
        for (int i = 1; i < vec.size(); ++i) {
            if (vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
    void traversal(TreeNode* node) {
        if (!node) return;
        traversal(node->left);
        vec.push_back(node->val);
        traversal(node->right);
    }

Java:

    List<Integer> nums;
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        nums = new ArrayList<>();
        traversal(root);
        for (int i = 1; i < nums.size(); ++i) {
            if (nums.get(i) <= nums.get(i - 1)) {
                return false;
            }
        }
        return true;
    }
    void traversal(TreeNode node) {
        if (node == null) return;
        traversal(node.left);
        nums.add(node.val);
        traversal(node.right);   
    }

4.1.2 版本Ⅱ、过渡版

在版本Ⅰ中,把二叉树转变为数组来判断,是最直观的,但其实不用转变成数组,可以在递归遍历的过程中直接判断是否有序。

C++:

    // int maxVal = INT_MIN;//错误
    long long maxVal = LONG_MIN;// 因为后台测试数据中有int最小值,所以都把maxVal改成了longlong最小值。
    bool isValidBST(TreeNode* root) {
        if (!root) return true;
        bool left = isValidBST(root->left);
        if (maxVal < root->val) {
            maxVal = root->val;
        } else return false;
        bool right = isValidBST(root->right);
        return left && right;
    }
Java:
    // Integer maxVal = Integer.MIN_VALUE;//error
    Long maxVal = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        boolean left = isValidBST(root.left);
        if (maxVal < root.val) {
            // maxVal = root.val;
            maxVal = Long.valueOf(root.val);
        } else return false;
        // if (maxVal >= root.val) {//if二选一
        //     return false;
        // } else maxVal = Long.valueOf(root.val);
        boolean right = isValidBST(root.right);
        return left && right;
    }

如果测试数据中有 longlong的最小值,怎么办?

不可能在初始化一个更小的值了吧。 建议避免 初始化最小值,如下方法取到最左面节点的数值来比较。

4.1.3 最终版

C++:

    TreeNode* pre = nullptr;
    bool isValidBST(TreeNode* root) {
        if (!root) return true;
        bool left = isValidBST(root->left);
        if (pre && pre->val >= root->val) return false;//if的条件不能反过来,否则会被初始的空值忽悠
        pre = root;
        bool right = isValidBST(root->right);
        return left && right;
    }

Java:

    TreeNode pre;
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        boolean left = isValidBST(root.left);
        if (pre != null && pre.val >= root.val) {//不好设置反向条件
            return false;
        }
        pre = root;
        boolean right = isValidBST(root.right);
        return left && right;
    }

4.2 法2、迭代

迭代法中序遍历稍加改动就可以了

4.2.1 普通迭代

C++:

    bool isValidBST(TreeNode* root) {
        if (!root) return true;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = nullptr;
        while (cur || !st.empty()) {
            if (cur) {
                st.push(cur);
                cur = cur->left;
            } else {
                cur = st.top();
                st.pop();
                if (pre && pre->val >= cur->val) {//条件要考虑周全!
                    return false;
                }
                pre = cur;
                cur = cur->right;
            }
        }
        return true;
    }

Java:

    TreeNode pre = null;
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            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;
    }

4.2.2 统一迭代

    TreeNode pre = null;
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        Deque<TreeNode> stack = new LinkedList<>();
        stack.offerFirst(root);
        while (!stack.isEmpty()) {
            TreeNode cur = stack.pop();
            if (cur != null) {
                if (cur.right != null) stack.push(cur.right);
                stack.addFirst(cur);
                stack.push(null);
                if (cur.left != null) stack.push(cur.left);
            } else {
                cur = stack.removeFirst();
                if (pre != null && pre.val >= cur.val) {
                    return false;
                }
                pre = cur;
            }
        }
        return true;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值