大厂必考二叉树算法-递归的深度使用

遍历二叉树

static class TreeNode{
        private int data;
        private TreeNode left;
        private TreeNode right;

        public TreeNode(int a) {
            this.data = a;
        }

        public TreeNode addL(int a) {
            TreeNode left = new TreeNode(a);
            this.left  = left;
            return left;
        }

        public TreeNode addR(int a) {
            TreeNode right = new TreeNode(a);
            this.right  = right;
            return right;
        }

    }

先序遍历

public static void pre(TreeNode node){
        if(node == null) {
            return;
        }
        System.out.print(node.data);
        pre(node.left);
        pre(node.right);
    }

中序遍历

public static void in(TreeNode node){
        if(node == null) {
            return;
        }
        in(node.left);
        System.out.print(node.data);
        in(node.right);
    }

后序遍历

public static void next(TreeNode node){
        if(node == null) {
            return;
        }
        next(node.left);
        next(node.right);
        System.out.print(node.data);
    }

递归序

先序、中序、后序遍历的区别只是打印节点的位置不同。遍历二叉树时每个节点都会经过3次,第一次经过就打印就是先序,第二次经过打印就是中序,第三次经过打印就是后序。

相同的树

力扣地址: https://leetcode.cn/problems/same-tree
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

public static boolean sameTree(TreeNode t1, TreeNode t2){
        if(t1 == null ^ t2 == null ) return false;
        if(t1 == null && t2 == null) return true;
        return t1.data == t2.data && sameTree(t1.left, t2.left)
                && sameTree(t1.right, t2.right);
    }

对称的树

力扣地址:https://leetcode.cn/problems/symmetric-tree

public static boolean mirrorTree(TreeNode root) {
        return mirrorTree(root, root);
    }

    private static boolean mirrorTree(TreeNode t1, TreeNode t2){
        if(t1 == null ^ t2 == null) return false;
        if(t1 == null && t2 == null) return true;
        return t1.data == t2.data
                && mirrorTree(t1.left, t2.right)
                && mirrorTree(t1.right, t2.left);
    }

获取二叉树的最大高度

力扣地址:https://leetcode.cn/problems/maximum-depth-of-binary-tree

public static int maxDeep(TreeNode root){
        if(root == null) return 0;
        return Math.max(maxDeep(root.left), maxDeep(root.right)) + 1;
    }

根据先序和中序结果还原二叉树

力扣地址:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal
重点: preorder 和 inorder 均 无重复 元素

思路:

  1. 先序列表中第一个元素一定是根节点root,在中序列表中找到根节点所在位置
  2. 将先序列表中左子树的列表 和 中序列表中左子树的列表进入递归 得到左子树的根节点,返回的节点就是root.left节点
  3. 将先序列表中右子树的列表 和 中序列表中右子树的列表进入递归 得到右子树的根节点,返回的节点就是root.right节点
public static TreeNode genByPreAndIn(int[] preArr, int[] inArr){
        //边界判断
        if(preArr == null || inArr == null || preArr.length != inArr.length){
            return null;
        }
        HashMap<Integer, Integer> inArrMap = new HashMap<>();
        for (int i = 0; i < inArr.length; i++) {
            inArrMap.put(inArr[i], i);
        }
        return genByPreAndIn(preArr, 0, preArr.length -1, inArr, 0, inArr.length - 1, inArrMap);
    }

    /**
     * 根据先序和中序的左、右边界返回创建二叉树节点
     */
    private static TreeNode genByPreAndIn(int[] preArr, int L1, int R1,
                                          int[] inArr, int L2, int R2,
                                          HashMap<Integer, Integer> inArrMap) {
        //如果L1 > R1 说明存在空的左子树或右子树
        if(L1 > R1) {
            return null;
        }
        TreeNode head = new TreeNode(preArr[L1]);
        if(L1 == R1) {
            return head;
        }
        int find = inArrMap.get(preArr[L1]);
        //递归左子树 计算先序列表中左子树的范围 和 中序列表中左子树的范围
        head.left = genByPreAndIn(preArr, L1 + 1, find - L2 + L1, inArr, L2, find - 1, inArrMap);
        //递归右子树 计算先序列表中右子树的范围 和 中序列表中右子树的范围
        head.right = genByPreAndIn(preArr, find -L2 + L1 + 1, R1, inArr, find + 1, R2, inArrMap);
        return head;
    }

层序遍历

力扣地址:https://leetcode.cn/problems/binary-tree-level-order-traversal-ii
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

思路:

  1. 创建一个链表实现的队列 第一次将root节点放入队列中
  2. 开始弹出root节点 并通过队列的size判断需要弹出多少次 ; 同时将弹出节点的左右节点放入队列中
  3. 弹出size个元素时就收集到当前层的结果中 并将结果插入到队列头节点
public List<List<Integer>> levelOrderBottom(TreeNode root) {
        if(root == null ) return new LinkedList<>();
        //定义队列收集二叉树节点
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        List<List<Integer>> result = new LinkedList<>();
        while (!queue.isEmpty()) {
        	//先得到这一次循环需要处理的层节点
            int size = queue.size();
            List<Integer> ans = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                TreeNode poll = queue.poll();
                ans.add(poll.data);
                if(poll.left != null) {
                    queue.offer(poll.left);
                }
                if(poll.right != null) {
                    queue.offer(poll.right);
                }
            }
            result.add(0,ans);
        }
        return result;
    }

平衡二叉树

力扣地址:https://leetcode.cn/problems/balanced-binary-tree/
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

思路:

  1. 要整个二叉树是平衡二叉树 只需要保证每个小的子树是平衡二叉树
  2. 使用递归收集每个子树是否平衡以及当前子树的高度
  3. 在上一层根据收集到的左右子树的信息 判断这一层子树是否平衡
/**
     * 收集每一个子树的信息返回到父节点判断
     */
    @Data
    @AllArgsConstructor
    private static class TreeInfo{
        private boolean isBalance;
        private int height;
    }

    public static boolean isBalance(TreeNode root){
        return processIsBalance(root).isBalance;
    }
    private static TreeInfo processIsBalance(TreeNode root){
        if(root == null) return new TreeInfo(true, 0);

        TreeInfo left = processIsBalance(root.left);
        TreeInfo right = processIsBalance(root.right);
        int height = Math.max(left.height, right.height) + 1;
        boolean isBalance = left.isBalance && right.isBalance && Math.abs(left.height - right.height) <= 1;
        return new TreeInfo(isBalance, height);
    }

搜索二叉树

力扣地址:https://leetcode.cn/problems/validate-binary-search-tree/
判断是否是搜索二叉树
搜索树定义:每个节点的左节点比他小 右节点比他大

中序遍历

思路:

  1. 中序遍历是左父右: 只要中序遍历的结果是递增的 一定是搜索二叉树

递归

思路:

  1. 使用递归收集每个子树是否是搜索树 收集当前树的最大值和最小值
  2. 在上一层根据收集到的左右子树的信息 判断这一层子树是否搜索二叉树:要比左树的最大值大 比右树的最小值小
public static boolean isSearch(TreeNode root){
        return processIsSearch(root).isBST;
    }

    private static class BSTInfo{
        private boolean isBST;
        private int max;
        private int min;

        public BSTInfo(boolean isBST, int max, int min) {
            this.isBST = isBST;
            this.max = max;
            this.min = min;
        }
    }

    private static BSTInfo processIsSearch(TreeNode root){
        //判空的处理交给上一层处理,不能返回最大值和最小值=0 因为父节点可能为负数,影响判断
        if(root == null) return null;
        BSTInfo left = processIsSearch(root.left);
        BSTInfo right = processIsSearch(root.right);
        //先把当前子树的最大值和最小值设置为当前节点的值
        int max = root.data;
        int min = root.data;
        //如果左树不为空 将最大值和最小值更新
        if(left != null) {
            max = Math.max(max, left.max);
            min = Math.min(min, left.min);
        }
        //如果右树不为空 将最大值和最小值更新
        if(right != null) {
            max = Math.max(max, right.max);
            min = Math.min(min, right.min);
        }
        //判断左右子树是否搜索二叉树
        boolean isBST = true;
        if(left != null && !left.isBST) {
            isBST = false;
        }
        if(right != null && !right.isBST) {
            isBST = false;
        }
        //判断收集上来的左节点最大值是否小于root.data
        boolean leftMaxLessX = left == null ? true : (left.max < root.data);
        boolean rightMinMoreX = right == null ? true : (right.min > root.data);
        if(!leftMaxLessX || !rightMinMoreX) {
            isBST = false;
        }
        return new BSTInfo(isBST, max, min);
    }

路径总和

力扣地址:https://leetcode.cn/problems/path-sum/
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。

public static boolean isPathSum = false;

    public static boolean hasPathSum(TreeNode root, int sum){
        if(root == null) return false;
        isPathSum = false;
        processHasPathSum(root, 0, sum);
        return isPathSum;
    }

    private static void processHasPathSum(TreeNode root, int preSum, int sum) {
        if(root.left == null && root.right == null) {
            //叶子节点
            if(preSum + root.data == sum) {
                isPathSum = true;
            }
            return;
        }
        preSum += root.data;
        //非叶子节点
        if(root.left != null) {
            processHasPathSum(root.left, preSum, sum);
        }
        if(root.right != null) {
            processHasPathSum(root.right, preSum, sum);
        }
    }

路径总和 II

力扣地址:https://leetcode.cn/problems/path-sum-ii
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

/**
     * 收集所有可能的路径和
     * @param root
     * @param sum
     * @return
     */
    public static List<List<Integer>> pathSum(TreeNode root, int sum) {
        List<List<Integer>> ans = new ArrayList<>();
        if (root == null) {
            return ans;
        }
        ArrayList<Integer> path = new ArrayList<>();
        process(root, path, 0, sum, ans);
        return ans;
    }

    /**
     *
     * @param x 当前节点
     * @param path 当前遍历的路径
     * @param preSum 当前遍历路径的累加和
     * @param sum 求和判断
     * @param ans 符合条件的结果
     */
    public static void process(TreeNode x, List<Integer> path, int preSum, int sum, List<List<Integer>> ans) {
        //是叶子节点的时候
        if (x.left == null && x.right == null) {
            //判断是否存在累加和
            if (preSum + x.data == sum) {
                path.add(x.data);
                ans.add(copy(path));
                //这里需要移除掉path中的叶子节点 因为这个路径已经计算完毕
                path.remove(path.size() - 1);
            }
            return;
        }
        //是非叶节点
        path.add(x.data);
        preSum += x.data;
        if (x.left != null) {
            //遍历左树
            process(x.left, path, preSum, sum, ans);
        }
        if (x.right != null) {
            process(x.right, path, preSum, sum, ans);
        }
        //最后需要移除掉最后一个元素 因为已经回到了父节点,说明父节点下面的路径已经执行完
        path.remove(path.size() - 1);
    }

    public static List<Integer> copy(List<Integer> path) {
        List<Integer> ans = new ArrayList<>();
        for (Integer num : path) {
            ans.add(num);
        }
        return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值