Leetcode 刷题笔记(十五) —— 二叉树篇之二叉树的修改与构造

系列文章目录

一、 数组类型解题方法一:二分法
二、数组类型解题方法二:双指针法
三、数组类型解题方法三:滑动窗口
四、数组类型解题方法四:模拟
五、链表篇之链表的基础操作和经典题目
六、哈希表篇之经典题目
七、字符串篇之经典题目
八、字符串篇之 KMP
九、解题方法:双指针
十、栈与队列篇之经典题目
十 一、栈与队列篇之 top-K 问题
十 二、二叉树篇之二叉树的前中后序遍历
十 三、二叉树篇之二叉树的层序遍历及相关题目
十 四、二叉树篇之二叉树的属性相关题目
更新中 …


前言

二叉树的修改:如翻转,合并等。
二叉树的构造:如根据前序遍历和中序遍历 或 后序遍历和中序遍历构造二叉树等。
刷题路线来自 :代码随想录

题录

226. 翻转二叉树

Leetcode 链接
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

示例 2:
输入:root = [2,1,3]
输出:[2,3,1]

示例 3:
输入:root = []
输出:[]

题解:
遍历二叉树,交换左右孩子结点的值

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        invertTree(root.left);
        invertTree(root.right);
        // 先序和后续遍历都可以,这里是后序遍历
        swap(root);
        return root;
    }
    // 交换
    public void swap(TreeNode root) {
        TreeNode node = root.left;
        root.left = root.right;
        root.right = node;
    }
}

106. 从中序与后序遍历序列构造二叉树

Leetcode 链接
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

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

示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]

题解:

  1. 后序遍历的最后一个结点为当前数组区间的子树的根结点。
  2. 在中序遍历数组,以根节点分割,左边就为左子树的全部结点,右边就为右子树的全部结点。我们知道了左子树的结点个数 n
  3. 在后序遍历数组中,从左开始前 n 个结点为左子树的结点,后边除去最后一个为根节点外都为右子树结点

使用递归的写法:

  1. 参数除两个遍历数组外,需要传入两个数组中左子树结点的区间和右子树结点的区间,区间统一为左闭右开
  2. 递归完左右子树后,返回根节点。作为上层递归的孩子结点
  3. 后序遍历数组 postorder 用来确定根结点,中序遍历数组 inorder 用根节点分割得到inorder 的左右子树区间和左子树结点个数
  4. 根据左子树结点个数,得到后序遍历数组 postorder 左右子树结点区间
  5. 中序遍历数组 inorder 没有结点了(每办法分割了)返回 null
  6. 中序遍历数组 inorder 只剩一个结点,返回该节点
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return build(inorder, 0, inorder.length, postorder, 0, postorder.length);
    }
    public TreeNode build(int[] inorder, int inLeft, int inRight,
                            int[] postorder, int postLeft, int postRight) {
        // 区间没有结点了
        if (inRight - inLeft == 0) {
        	return null;        
        }               
        // 只剩一个结点,下标为 inLeft(因为区间为左闭右开)
        if (inRight - inLeft == 1) {
            return new TreeNode(inorder[inLeft]);
        }
        
        // 后序遍历最后一个元素,为当前区间子树的根结点
        int rootVal = postorder[postRight - 1];
        TreeNode root = new TreeNode(rootVal);
        // 分割点下标
        int rootIndex = -1;
        // 在中序遍历中找到根结点,左边为左子树结点,右边为右子树结点
        for (int i = inLeft; i < inRight; i++) {
            if (inorder[i] == rootVal) {
                rootIndex = i;
                break;
            }
        }
        // 左子树结点个数:rootIndex - inLeft, 那么后序遍历子数组中:
        //[postLeft, postLeft + 左子树结点个数)为左子树的结点,
        //[postLeft + 左子树结点个数, postRight - 1(最后一个结点是根节点,减去1)) 为右子树区间
        root.left = build(inorder, inLeft, rootIndex, 
        		postorder, postLeft, postLeft + (rootIndex - inLeft));
        root.right = build(inorder, rootIndex + 1, inRight,
                postorder,postLeft + (rootIndex - inLeft) , postRight - 1);
        // 返回当前区间子树的根节点
        return root;
    }
}

105. 从前序与中序遍历序列构造二叉树

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

示例 1:
输入: 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]
题解:
同上题,不同点在于根节点在前序遍历数组的最左边,所以下层递归的区间稍有改变

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder, 0, preorder.length, inorder, 0, inorder.length);
    }
    public TreeNode build(int[] preorder, int preLeft, int preRight,
                            int[] inorder, int inLeft, int inRight) {
		// 没有结点了                            
        if (inRight - inLeft == 0) {
            return null;
        }
        // 只剩一个结点
        if (inRight - inLeft == 1) {
            return new TreeNode(inorder[inLeft]);
        }
        int rootVal = preorder[preLeft];
        // 当前区间子树的根节点
        TreeNode root = new TreeNode(rootVal);
        // 分割点
        int rootIndex = -1;
        for (int i = inLeft; i < inRight; i++) {
            if (inorder[i] == rootVal) {
            	// 根结点下标就为分割点
                rootIndex = i;
                break;
            }
        }
        // 左子树结点个数:rootIndex - inLeft, 那么后序遍历子数组中:
        //左子树的结点区间:[preLeft+ 1(第一个为根节点,所以 +1), preLeft+ 1 + 左子树结点个数)
        //右子树结点区间:[preLeft+ 1 + 左子树结点个数, preRight) 
        root.left = build(preorder, preLeft + 1, preLeft + 1 + (rootIndex - inLeft), 
                        inorder, inLeft, rootIndex);
        root.right = build(preorder, preLeft + 1 + (rootIndex - inLeft), preRight,
                        inorder, rootIndex + 1, inRight);
        return root;
    }
}

654. 最大二叉树

Leetcode 链接
给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。

示例 1:
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:

  • [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    • [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
      • 空数组,无子节点。
      • [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
        • 空数组,无子节点。
        • 只有一个元素,所以子节点是一个值为 1 的节点。
    • [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
      • 只有一个元素,所以子节点是一个值为 0 的节点。
      • 空数组,无子节点。

示例 2:
输入:nums = [3,2,1]
输出:[3,null,2,null,1]
题解:
上边两题会了,那这道题就很简单了。
找到数组中最大数作为分割点,左边为左子树,右边为右子树

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums, 0, nums.length);
    }
    public TreeNode build(int[] nums, int left, int right) {
        if (right - left <= 0) {
            return null;
        }
        if (right - left == 1) {
            return new TreeNode(nums[left]);
        }
        int rootVal = Integer.MIN_VALUE;
        int maxIndex = -1;
        for (int i = left; i < right; i++) {
            if (nums[i] > rootVal) {
                rootVal = nums[i];
                maxIndex = i;         
            }
        }
        TreeNode root = new TreeNode(rootVal);
        root.left = build(nums, left, maxIndex);
        root.right = build(nums, maxIndex + 1, right);
        return root;
    }
}

617. 合并二叉树

Leetcode 链接
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。

示例 1:
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

示例 2:
输入:root1 = [1], root2 = [1,2]
输出:[2,2]
题解:
返回的二叉树不和合并的树结点不重叠

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        return build(root1, root2);
    }
    public TreeNode build(TreeNode root1, TreeNode root2) {
        if (root1 == null && root2 == null) {
            return null;
        }
        TreeNode root = null;
        if (root1 == null) {
        	// 第一棵节点为空
            root =  new TreeNode(root2.val);
            // root1 为空,root1.left 会空指针异常,递归时左孩子结点继续为 null
            root.left = build(root1, root2.left);
            root.right = build(root1, root2.right);         
        } else if (root2 == null) {
        	// 第二棵节点为空
            root = new TreeNode(root1.val);
            root.left = build(root1.left, root2);
            root.right = build(root1.right, root2);
        } else {
        	// 两颗树结点都不为 null,val 相加
            root = new TreeNode(root1.val + root2.val);
            root.left = build(root1.left, root2.left);
            root.right = build(root1.right, root2.right);
        }
        return root;
    }
}

为了代码简单一颗树为空,可以直接返回另一棵树的子树根结点,将子树接到合并后的树上

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        return build(root1, root2);
    }
    public TreeNode build(TreeNode root1, TreeNode root2) {
        if (root1 == null) {
        	// 返回不为空的子树根节点
            return root2;
        }
        if (root2 == null) {
            return root1;
        }
        TreeNode root = new TreeNode(root1.val + root2.val);
        root.left = build(root1.left, root2.left);
        root.right = build(root1.right, root2.right);
        return root;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值