• 二叉树习题总结


    目录

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

    1.题目

    2.思路图解

    3.代码

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

    1.题目

    2.思路图解

    3.代码

    三.二叉树的的最大路径和

    1.题目

    2.思路图解

    3.代码

    四.括号生成

    1.题目

    2.思路图解

    3.代码

    五. 不同的二叉搜索树

    1.题目

    2.思路图解

    3.代码

    (1)优化前

    (2)优化后

    六.验证二叉搜索树

    1.题目

    2.思路图解

    3.代码

    七.二叉树展开为链表

    1.题目

    2.思路图解

    3.代码

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

    1.题目

    给定一个二叉树的中序与后序遍历结果,请你根据两个序列构造符合这两个序列的二叉树。

    示例:

    例如输入[2,1,4,3,5],[2,4,5,3,1]时,

    根据中序遍历的结果[2,1,4,3,5]和后序遍历的结果[2,4,5,3,1]可构造出二叉树{1,2,3,#,#,4,5}

    2.思路图解

    因为后序遍历的最后一个元素是根节点,所以我们可以根据后序序列确定根节点,然后再从中序遍历中寻找该根节点,然后划分区间,最后根据区间递归构造当前根节点的左右子树即可。

    3.代码

    1. public TreeNode buildTree (int[] inorder, int[] postorder) {
    2. // write code here
    3. return build(inorder, 0, inorder.length, postorder, 0, postorder.length);
    4. }
    5. public TreeNode build(int[] inorder, int inLeft, int inRight, int[] postorder, int postLeft, int postRight) {
    6. //说明当前区间的左右子树已经构造完成
    7. if(inRight-inLeft<1) {
    8. return null;
    9. }
    10. //说明当前子树的左边只剩下一个结点,直接构造为根节点
    11. if(inRight-inLeft==1) {
    12. return new TreeNode(inorder[inLeft]);
    13. }
    14. //在中序数组中找根节点所在的位置
    15. int pos = 0;
    16. for(int i=inLeft; i<inRight; i++) {
    17. if(inorder[i] == postorder[postRight-1]) {
    18. pos = i;
    19. break;
    20. }
    21. }
    22. TreeNode root = new TreeNode(inorder[pos]);
    23. //找到根节点所在位置后,开始划分中序数组和后序数组的区间
    24. root.left = build(inorder, inLeft, pos, postorder, postLeft, postLeft+(pos-inLeft));
    25. root.right = build(inorder, pos+1, inRight, postorder, postLeft+(pos-inLeft), postRight-1);
    26. return root;
    27. }

     

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

    1.题目

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

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

    示例:

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

    2.思路图解

    划分区间的思想:这里首先使用map来保存中序数组的值和下标,方便之后找以中序位置划分区间;然后递归构造二叉树,当前序的左边界=右边界,说明当前区间已经构造完成,直接返回,否则就先根据前序(前序的子区间的第一个结点为根结点)构造出根结点,然后再递归构造该根根结点的左右子树,最后返回根结点即可。

     

    3.代码

    1. Map<Integer, Integer> map = new HashMap<>();
    2. public TreeNode buildTree(int[] preorder, int[] inorder) {
    3. for(int i=0; i<inorder.length; i++) {
    4. map.put(inorder[i], i);
    5. }
    6. return dfs(preorder, 0,preorder.length, inorder, 0, inorder.length);
    7. }
    8. public TreeNode dfs(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight){
    9. if(inRight-inLeft==0) {
    10. return null;
    11. }
    12. //获取中序遍历的位置
    13. int pos = map.get(preorder[preLeft]);
    14. //划分区间并构造根结点
    15. TreeNode root = new TreeNode(inorder[pos]);
    16. root.left = dfs(preorder, preLeft+1, preLeft+(pos-inLeft)+1, inorder, inLeft, pos);
    17. root.right = dfs(preorder, preLeft+(pos-inLeft)+1, preRight, inorder, pos+1, inRight);
    18. return root;
    19. }

     

     

    三.二叉树的的最大路径和

    1.题目

    路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

    路径和 是路径中各节点值的总和。

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

    示例:

    2.思路图解

    3.代码

    1. int maxRes = Integer.MIN_VALUE;
    2. public int maxPathSum(TreeNode root) {
    3. dfs(root);
    4. return maxRes;
    5. }
    6. public int dfs(TreeNode root) {
    7. //空结点不做贡献
    8. if(root == null) {
    9. return 0;
    10. }
    11. //统计左右子树分别的贡献,如果小于0就不统计
    12. int left = Math.max(dfs(root.left),0);
    13. int right = Math.max(dfs(root.right),0);
    14. //判断是否当前路径的值比保存的结果值大
    15. maxRes = Math.max(maxRes, root.val+left+right);
    16. //返回当前子树中的最大路径和
    17. return root.val+Math.max(left, right);
    18. }

    四.括号生成

    1.题目

    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

    示例:

    输入:n = 3
    输出:["((()))","(()())","(())()","()(())","()()()"]

    2.思路图解

    首先递归处理左括号,在处理完左括号后,右括号的处理需要依据左括号进行处理,当左括号处理的个数多于右括号处理的个数时,再处理右括号,相当于左括号优先匹配,因为要先有左括号后,然后右括号才能匹配,处理图解如下:

    3.代码

    1. public List<String> generateParenthesis(int n) {
    2. List<String> res = new ArrayList<>();
    3. dfs(n, n, new StringBuilder(), res);
    4. return res;
    5. }
    6. public void dfs(int left, int right, StringBuilder sb, List<String> res) {
    7. if(left==0 && right==0) {
    8. res.add(new String(sb));
    9. return;
    10. }
    11. if(left>0) {
    12. sb.append('(');
    13. dfs(left-1, right, sb, res);
    14. sb.deleteCharAt(sb.length()-1);
    15. }
    16. if(right>left) {
    17. sb.append(')');
    18. dfs(left, right-1, sb, res);
    19. sb.deleteCharAt(sb.length()-1);
    20. }
    21. }

    五. 不同的二叉搜索树

    1.题目

    给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

    示例:

    2.思路图解

    二叉搜索树就是左子树的结点<当前根结点<右子树的结点;因为每一个都可能作为根结点,所以每次计算以当前结点为根结点能够构成的所有二叉搜索树的个数,之后求所有结果和即可;对于当前结点求二叉搜索树个数的方式是:先求当前结点左边结点组成二叉搜索树的个数,再求当前结点右边结点组成二叉搜索树的个数,最后左边*右边的个数就是当前结点的所有二叉搜索树构成的个数。

    优化:由于每一个元素都会作为根结点,所以有些子树中的二叉搜索树多次重复求解,这里借助map集合来保存求得的二叉搜索树,如果当前结点的二叉搜索树已经求过,就直接在map中取出即可,否则就添加到map中。

    3.代码

    (1)优化前

    1. public int numTrees(int n) {
    2. //根结点只有1个或0个
    3. if(n==0 || n==1) {
    4. return 1;
    5. }
    6. int count = 0;
    7. for(int i=1; i<=n ;i++) {
    8. //计算以当前结点为根节点的左右子树可以构成的个数
    9. int leftCount = numTrees(i-1);
    10. int rightCount = numTrees(n-i);
    11. count += leftCount*rightCount;
    12. }
    13. return count;
    14. }

    (2)优化后

    1. //设置成全局变量,保存记录
    2. HashMap<Integer,Integer> map = new HashMap<>();
    3. public int numTrees(int n) {
    4. //根结点只有1个或0个
    5. if(n==0 || n==1) {
    6. return 1;
    7. }
    8. //说明以当前为根的二叉搜索树的个数已经查找过了
    9. if(map.containsKey(n)) {
    10. return map.get(n);
    11. }
    12. int count = 0;
    13. for(int i=1; i<=n ;i++) {
    14. //计算以当前结点为根节点的左右子树可以构成的个数
    15. int leftCount = numTrees(i-1);
    16. int rightCount = numTrees(n-i);
    17. count += leftCount*rightCount;
    18. }
    19. map.put(n, count);
    20. return count;
    21. }

    六.验证二叉搜索树

    1.题目

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

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

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

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

    2.思路图解

    根据二叉搜索树的性质:左<当前<右,中序结果是有序的,所以在查找的时候保留前一个结点,之后当前结点的值与前一个结点的值进行比较,如果前一个结点的值>=当前结点的值,说明不符合结果,返回false;最终处理的方式是先处理左子树,如果左子树不满足条件,直接返回false;若左子树有序,则处理当前结点,之后再处理右子树。

    3.代码

    1. TreeNode pre = null;
    2. public boolean isValidBST(TreeNode root) {
    3. if(root == null) {
    4. return true;
    5. }
    6. //说明左子树不满足二叉树性质,直接返回
    7. if(!isValidBST(root.left)) {
    8. return false;
    9. }
    10. //判断当前结点与前一个结点的大小
    11. if(pre!=null && pre.val>= root.val) {
    12. return false;
    13. }
    14. pre = root;
    15. //无论右子树的情况,直接返回
    16. return isValidBST(root.right);
    17. }

     

    七.二叉树展开为链表

    1.题目

    给你二叉树的根结点 root ,请你将它展开为一个单链表:

    展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
    展开后的单链表应该与二叉树 先序遍历 顺序相同。

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

    示例:

     

    2.思路图解

    先前序遍历保存所有结点,然后再以前一个节点为前驱(pre),先修改的pre.left为空,然后再让pre.right指向当前结点(cur),最后所得结果就是单链表。

    3.代码

    1. public void flatten(TreeNode root) {
    2. List<TreeNode> list = new ArrayList<>();
    3. preorder(root, list);
    4. //第一个节点为前驱结点,修改前一个节点的left为null, right指向当前结点
    5. for(int i=1; i<list.size(); i++) {
    6. TreeNode pre = list.get(i-1);
    7. TreeNode cur = list.get(i);
    8. pre.left = null;
    9. pre.right = cur;
    10. }
    11. }
    12. public void preorder(TreeNode root, List<TreeNode> list){
    13. if(root == null) {
    14. return;
    15. }
    16. //根左右
    17. list.add(root);
    18. preorder(root.left, list);
    19. preorder(root.right, list);
    20. }

     

  • 相关阅读:
    计算机毕设 SpringBoot+Vue校园博客系统 博客管理系统 开源博客系统 个人博客系统Java Vue MySQL数据库 远程调试 代码讲解
    单元测试该怎么写
    零基础可以考软考高级吗?
    Android 实现开机自启APP
    基于python tornado实现的简易图床
    crypto 加解密库简介与测试【GO 常用的库】
    Vue3 第二十四篇:集成vue.draggable
    Linux内核中的锁
    分布式ID生成算法——雪花算法
    读写锁三种关系的证明(读者和读者互补影响、写者和写者互斥、读者和写者互斥)
  • 原文地址:https://blog.csdn.net/weixin_47651920/article/details/124856115