打败算法第三节之二叉树


前言

今天准备了五道题,一起来打卡学习吧!


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

  • 中等,105题 从前序与中序遍历序列构造二叉树
  • 题目描述:给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
  • 输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
    输出: [3,9,20,null,null,15,7]
//二叉树数据结构
public class TreeNode {
      int val;
      TreeNode left;
      TreeNode right;
      TreeNode() {}
      TreeNode(int val) { this.val = val; }
      TreeNode(int val, TreeNode left, TreeNode right) {
          this.val = val;
          this.left = left;
          this.right = right;
      }
 }
  • 思路:先序遍历为头左右,中序遍历为左头右,所以先序数组的第一个节点必定为整棵树的头节点,在中序数组中找到头节点位置find(也可在主函数中先用HashMap保存,但需额外空间),那么中序数组中头节点的左边即是整棵树的左子树,头节点的右边即是整棵树的右子树,递归以上流程,形成二叉树。
public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length == 0 || inorder.length == 0 || preorder.length != inorder.length){
            return null;
        }
        return build(preorder, 0 , preorder.length-1,inorder,0,inorder.length-1);
    }
	//根据先序结果是pre[L1...R1],中序结果是in[L2...R2]建出整棵树并返回头节点  
public TreeNode build(int[] preorder, int L1, int R1, int[] inorder, int L2, int R2){
		//需要保证以下条件,或者用L2和R2判断也可。
        if(L1 > R1){
            return null;
        }
        TreeNode head = new TreeNode(preorder[L1]);
        if(L1 == R1){
            return head;
        }
        int find = L2;
        while(preorder[L1] != inorder[find]){
            find ++;
        }
        head.left = build(preorder, L1+1, find - L2 + L1,inorder,L2,find-1);
        head.right = build(preorder, find-L2+L1+1,R1,inorder,find+1,R2);
        return head;
    }

2.二叉树的层序遍历II

  • 中等,107题 二叉树的层序遍历II
  • 题目描述:给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
    输入:root = [3,9,20,null,null,15,7]
    输出:[[15,7],[9,20],[3]]
  • 思路:准备一个队列,先把头节点放到队列中,当队列不为空时循环执行以下逻辑:准备一个装入每层遍历结果的集合,取出当前队列长度size,循环遍历size次,拿出队列当前的节点,放入集合中,如果当前节点有左孩子则将左孩子放入队列中;如果当前节点有右孩子则将右孩子放入队列中,循环一次则把结果放入到返回的总集合中。
  • 时间复杂度分析:由于内层循环为常数,只外层循环与二叉树的节点有关,所以时间复杂度为O(N)
public List<List<Integer>> levelOrderBottom(TreeNode root) {
 	 // 要求从底向上遍历,所以使用linkedlist
        List<List<Integer>> ans = new LinkedList<>();
        if(root == null){
            return ans;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            List<Integer> curAns = new LinkedList<>();
            // 收集每层节点
            for(int i = 0;i<size;i++){
                TreeNode curNode = queue.poll();
                curAns.add(curNode.val);
                if(curNode.left != null){
                    queue.add(curNode.left);
                }
                if(curNode.right != null){
                    queue.add(curNode.right);
                }
            }
            ans.add(0, curAns);
        }
        return ans;
  }

3.平衡二叉树

  • 简单,110题 平衡二叉树
  • 题目描述:给定一个二叉树,判断它是否是平衡二叉树
    输入:root = [3,9,20,null,null,15,7]
    输出:true
  • 思路:平衡二叉树的定义:左右子树高度之差不超过1,且其左右子树也必须为平衡二叉树。据此我们定义一个数据结构,其中包含当前节点是否平衡和当前节点的高度。如果当前节点为空的话则认为是一棵深度为0的平衡二叉树,递归调用左右子树,并根据结果判断当前是否为平衡二叉树。
public class Info{
        boolean isBalanced;
        int depth;
        public Info(boolean is,int de){
            isBalanced = is;
            depth = de;
        }
    }
    public boolean isBalanced(TreeNode root) {
        return getInfo(root).isBalanced;
    }
    public Info getInfo(TreeNode root){
        if(root == null){
            return new Info(true, 0);
        }
        Info leftInfo = getInfo(root.left);
        Info rightInfo = getInfo(root.right);
        boolean isBalanced = leftInfo.isBalanced && rightInfo.isBalanced && Math.abs(leftInfo.depth - rightInfo.depth) <= 1;
        int depth = Math.max(leftInfo.depth,rightInfo.depth) + 1;
        return new Info(isBalanced, depth);
    }

4.路径之和

  • 简单,112题 路径之和
  • 题目描述:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。叶子节点 是指没有子节点的节点。
    输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
    输出:true
  • 思路:准备一个全局变量,用于记录结果,如果根节点为空时,则直接返回false。定义一个遍历到当前节点时之前所有节点的总和,在递归调用时,判断当前节点是否为叶子节点,如果为叶子节点,则需要加上叶子节点与目标和进行比较,如果相等,则修改返回值为true,否则直接返回;如果不是叶子节点,则用当前总和加上当前节点的值来更新当前总和,然后依次调用左右子树。
public static boolean isSum = false;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null){
            return false;
        }
        // 每次执行都需要重新赋值
        isSum = false;
        pathSum(root, 0, targetSum);
        return isSum;
    }
    public void pathSum(TreeNode root, int curSum,int targetSum){
        if(root.left == null && root.right == null){
            if(root.val + curSum == targetSum){
                isSum = true;
            }
            return;
        }
        curSum += root.val;
        if(root.left != null){
            pathSum(root.left, curSum, targetSum);
        }
        if(root.right != null){
            pathSum(root.right, curSum, targetSum);
        }
    }

5.路径之和II

  • 中等,113题 路径之和II
  • 题目描述:给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。
    输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
    输出:[[5,4,11,2],[5,8,4,5]]
  • 思路:准备两个集合,存放结果集合ans和当前已经经过的节点集合curAns,如果当前节点为叶子节点,判断是否符合题意,符合的话将curAns加入到ans中,不符合的话直接返回;如果当前节点不是叶子节点,则将当前节点的值加入到curSum,并将当前节点放入到curAns中,再依次判断左右子树。
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null){
            return ans;
        }
        List<Integer> curAns = new ArrayList<>();
        curPathSum(root, ans, curAns, 0, targetSum);
        return ans;
    }
    public void curPathSum(TreeNode root,List<List<Integer>> ans, List<Integer> curAns, int curSum, int targetSum){
        if(root.left == null && root.right == null){
            if(root.val + curSum == targetSum){
                curAns.add(root.val);
                // 这里另外复制一个集合的原因是:如果将原集合加入到结果中,那么在之后的逻辑中再操作这个集合会把结果改变,所以必须复制一个集合,这样两个集合两个内存空间,不影响
                ans.add(copyList(curAns));
                curAns.remove(curAns.size()-1);
            }
            return;
        }
        curSum += root.val;
        curAns.add(root.val);
        if(root.left != null){
            curPathSum(root.left, ans, curAns, curSum, targetSum);
        }
        if(root.right != null){
            curPathSum(root.right, ans, curAns, curSum, targetSum);
        }
        curAns.remove(curAns.size()-1); 
    }
    public ArrayList copyList(List<Integer> list){
        ArrayList<Integer> copyList = new ArrayList<>();
        for(int num : list){
            copyList.add(num);
        }
        return copyList;
    }

总结

欢迎大家讨论,如有错误还请指正!(恭喜中国跳水队包揽10米跳水金银牌!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值