二叉树总结

目录

1、二叉树的递归遍历

2、二叉树的迭代遍历(非递归)

2.1 前序遍历

2.2 中序遍历

2.3 后序遍历

3、二叉树的层序遍历

3.1 二叉树的层序遍历II

3.2 二叉树的右视图

3.3 二叉树的层平均值

3.4 N叉树的层序遍历

3.5 在每个树行中找最大值

3.6 填充每个节点的下一个右侧节点指针

3.7 填充每个节点的下一个右侧节点指针II

3.8 二叉树的最大深度

3.9 二叉树的最小深度

4、反转二叉树

 5、对称二叉树

 6、平衡二叉树

7、二叉树的所有路径

8、左叶子之和

9、找树左下角的值

10、路径总和

11、构造二叉树

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

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

11.3、最大二叉树

 12、合并二叉树

 13、二叉搜索树

13.1 二叉搜索树中的搜索

 13.2、验证二叉搜索树

13.3、二叉搜索树的最小绝对差

13.4 二叉搜索树中的众数

13.5 二叉树的最近公共祖先

13.6  二叉搜索树的最近公共祖先

13.7 二叉搜索树中的插入操作

13.8 删除二叉搜索树中的节点

13.9 修剪二叉搜索树

 13.10 将有序数组转换为二叉搜索树

13.11 把二叉搜索树转换为累加树

  • 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。

  • 普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算

  • 二叉搜索树的属性,一定是中序了,要不白瞎了有序性了

1、二叉树的递归遍历

// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null) {
            return;
        }
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
    }
}
// 中序遍历·递归·LC94_二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inorder(root.left, list);
        list.add(root.val);             // 注意这一句
        inorder(root.right, list);
    }
}
// 后序遍历·递归·LC145_二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);             // 注意这一句
    }
}

2、二叉树的迭代遍历(非递归)

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

2.1 前序遍历

前序遍历是根左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right != null){
                stack.push(node.right);
            }
            if (node.left != null){
                stack.push(node.left);
            }
        }
        return result;
    }
}

2.2 中序遍历

中序遍历是左根右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        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();
               result.add(cur.val);
               cur = cur.right;
           }
        }
        return result;
    }
}

2.3 后序遍历

后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:

所以后序遍历只需要前序遍历的代码稍作修改就可以了,代码如下:

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.left != null){
                stack.push(node.left);
            }
            if (node.right != null){
                stack.push(node.right);
            }
        }
        Collections.reverse(result);
        return result;
    }
}

3、二叉树的层序遍历

/**
 * Definition for a binary tree node.
 * 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;
 *     }
 * }
 */
class Solution {
    List<List<Integer>> resList = new ArrayList<List<Integer>>();

    public List<List<Integer>> levelOrder(TreeNode root) {
        fun(root,0);
        return resList;
    }
    /**
     * 整体逻辑就是:当reslist.size < deep时,那就创建list
     * 等于或者大于时,就该往相应的list里面添加元素了。
     * @param root
     * @param deep
     */
    private void fun(TreeNode root,int deep) {
        //思路:每一层对应一个list,最后的结果是一个大的list
        if (root == null) return ;
        deep++;  //代表当前在第几层
        if (resList.size() < deep){
            //当层级增加时,存放每层元素的集合数也增加,即当前在第几层那么reslist里面就有几个list
            List<Integer> list = new ArrayList<Integer>();
            resList.add(list);
        }
        resList.get(deep -1).add(root.val);
        fun(root.left,deep);
        fun(root.right,deep);
    }
   //队列实现
   public List<List<Integer>> levelOrder1(TreeNode root) {
        List<List<Integer>> resList = new ArrayList<List<Integer>>();
        if(root == null)return resList;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root); //队列尾部插入元素

        while (!que.isEmpty()){
            List<Integer> list = new ArrayList<Integer>();
            int len  = que.size();
            while (len > 0){
                TreeNode tmpNode = que.poll();
                list.add(tmpNode.val);
                if (tmpNode.left !=null)que.offer(tmpNode.left);
                if (tmpNode.right != null)que.offer(tmpNode.right);
                len--;
            }
            resList.add(list);
        }
        return resList;
    }

}

3.1 二叉树的层序遍历II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历),相对于二叉树的层序遍历,就是最后把result数组反转一下就可以了。

3.2 二叉树的右视图

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

思路:层序遍历,遍历每一层的最后一个节点

public class Case199 {
    //递归
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> resList = new ArrayList<>();
        fun(root,0,resList);
        return resList;
    }

    private void fun(TreeNode root,int deep,List<Integer>resList) {
       if (root == null)return;
       if (deep == resList.size()){ //这个深度首次达到
           resList.add(root.val);
       }
       fun(root.right,deep + 1,resList);
       fun(root.left,deep + 1,resList); //这个妙啊!!
    }


    //层序遍历,每次返回每层的最后一个节点
    public List<Integer> rightSideView1(TreeNode root) {
        Queue<TreeNode> que = new LinkedList<>();
        List<Integer> list = new ArrayList<>();
        if (root == null) return list;
        que.offer(root);
        while (!que.isEmpty()) {
            int len = que.size();
            while (len > 0){
                TreeNode tmp = que.poll();
                if (tmp.left != null) que.offer(tmp.left);
                if (tmp.right != null) que.offer(tmp.right);
                len--;
                if (len == 0)list.add(tmp.val);
            }
        }
        return list;
    }
}

3.3 二叉树的层平均值

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。

public class Case637 {

    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> list = new ArrayList<>();
        if (root == null)return list;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()){
            int len = queue.size();
            Double sum = 0.0;
            while (len > 0){
                TreeNode tem = queue.poll();
                sum += tem.val;
                if (tem.left !=null)queue.offer(tem.left);
                if (tem.right != null)queue.offer(tem.right);
                len--;
                if (len == 0){
                    Double res = sum / len;
                    list.add(res);
                }
            }
        }
        return list;
    }
}

3.4 N叉树的层序遍历

给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。例如,给定一个 3叉树 :

返回其层序遍历:[ [1], [3,2,4], [5,6] ]

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/
public class Case429 {

    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null ){
            return res;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
            List<Integer> list = new ArrayList<>();
            int len  = queue.size();
            while (len > 0){
                Node p = queue.poll();
                list.add(p.val);
                if (p.children != null){
                    for (Node child : p.children) {
                        queue.offer(child);
                    }
                }
                len--;
            }
            res.add(list);
        }
        return res;
    }
}

3.5 在每个树行中找最大值

public class Case515 {
    /**
     * 给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
     * @param root
     * @return
     */
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        if (root == null) return res;
        queue.offer(root);

        while (!queue.isEmpty()) {
            int len = queue.size();
            int max = Integer.MIN_VALUE;
            for (int i = 0; i < len; i++) {
                TreeNode p = queue.poll();
                max = Math.max(p.val, max);
                if (p.left != null)queue.offer(p.left);
                if (p.right != null)queue.offer(p.right);
            }
            res.add(max);
        }
    return res;
    }
}

3.6 填充每个节点的下一个右侧节点指针

public class Case116 {

    public Pnode connect(Pnode root) {
        if (root == null) return null;
        Queue<Pnode> queue = new LinkedList<Pnode>();
        queue.offer(root);
        while (!queue.isEmpty()){
            int len = queue.size();
            while (len > 0){
                Pnode tem = queue.poll();
                len--;
                if (len == 0){
                    tem.next = null;
                }else {
                    tem.next = queue.peek();
                }
                if (tem.left != null)queue.offer(tem.left);
                if (tem.right != null)queue.offer(tem.right);
            }
        }
        return root;
    }
}

3.7 填充每个节点的下一个右侧节点指针II

与上题一样

3.8 二叉树的最大深度

给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。

返回它的最大深度 3 。

使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null)   return 0;
        Queue<TreeNode> que = new LinkedList<>();
        que.offer(root);
        int depth = 0;
        while (!que.isEmpty())
        {
            int len = que.size();
            while (len > 0)
            {
                TreeNode node = que.poll();
                if (node.left != null)  que.offer(node.left);
                if (node.right != null) que.offer(node.right);
                len--;
            }
            depth++;
        }
        return depth;
    }

    //递归
    public int maxDepth(TreeNode root) {
        if (root == null)return 0;

        int lDepth = maxDepth(root.left);
        int rDepth = maxDepth(root.right);
        int max = Math.max(lDepth,rDepth) + 1;
        return max;
    }
}

3.9 二叉树的最小深度

public class Case111 {

    /**
     * 与求最大深度还不一样,
     * @param root
     * @return
     */
    public int minDepth(TreeNode root) {
       
        if(root == null) return 0;
        int m1 = minDepth(root.left);
        int m2 = minDepth(root.right);
        //1.如果左孩子和右孩子有为空的情况,直接返回m1+m2+1
        //2.如果都不为空,返回较小深度+1
   
        return root.left == null || root.right == null ? m1 + m2 + 1 : Math.min(m1,m2) + 1;
    }

    // 层序遍历
    public int minDepth1(TreeNode root){
        if (root == null) return 0;
        Queue<TreeNode>queue = new LinkedList<>();
        queue.offer(root);
        int depth = 0;
        while (!queue.isEmpty()){
            depth++; //每遍历完一层深度+1
            int len = queue.size();
            while (len > 0) {
                TreeNode p = queue.poll();
                if (p.left == null && p.right == null) return depth; //到达叶子节点了
                if (p.left != null) queue.offer(p.left);
                if (p.right != null) queue.offer(p.right);
                len--;
            }
        }
        return depth;
    }
}

4、反转二叉树

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

public class Case226 {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()){
            int len = queue.size();
            while (len > 0){
                TreeNode p = queue.poll();
                swap(p);
                if (p.left != null)queue.offer(p.left) ;
                if (p.right != null)queue.offer(p.right);

                len--;
            }
        }
        return root;
    }
    private void swap(TreeNode p) {
        TreeNode temp = p.left;
        p.left = p.right;
        p.right = temp;
    }

    //递归
    public TreeNode invertTree1(TreeNode root) {
        if (root == null) return null;
        invertTree1(root.left);
        invertTree1(root.right);
        swap(root);
        return root;
    }

 5、对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

/**
 * 简单题并不简单
 * 按照普通层序遍历法,是行不通的,不应该一个一个出队列
 * 而是两个两个出队列
 */
public class Case101 {
    public boolean isSymmetric1(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        queue.offerFirst(root.left);
        queue.offerLast(root.right);
        while (!queue.isEmpty()){
            TreeNode a = queue.pollFirst();
            TreeNode b = queue.pollLast();
            if (a == null && b == null){
                continue;
            }

           if (a == null && b != null)return false;
           if (a != null && b == null)return false;
           if (a.val != b.val)return false;

           queue.offerFirst(a.left);
           queue.offerFirst(a.right);
           queue.offerLast(b.right);
           queue.offerLast(b.left);
        }
        return true;
    }

    //递归法
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left,root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {
        if (left == null && right != null)return false;
        if (left != null && right == null) return false;
        if (left == null && right == null)return true;
        if (left.val != right.val) return false;

        //比较外侧
        boolean compareOutside = compare(left.left,right.right);
        //比较内测
        boolean compareInside = compare(left.right,right.left);
        return compareInside && compareOutside;
    }
}

 6、平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

public class Case110 {

    public boolean isBalanced1(TreeNode root) {
        if (root == null) {
            return true;
        }
        int res = getHeight1(root);
        return res == -1 ? false : true;

    }

    private int getHeight1(TreeNode root) {
        if (root == null) return 0;
        int m = getHeight1(root.left);
        if (m == -1) return -1;
        int n = getHeight1(root.right);
        if (n == -1) return -1;
        if (Math.abs(m - n) > 1) return -1;

        return Math.max(m, n) + 1;
    }

7、二叉树的所有路径

给定一个二叉树,返回所有从根节点到叶子节点的路径。说明: 叶子节点是指没有子节点的节点。

这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。

前序遍历以及回溯的过程如图:

 

public class Case257 {

    public List<String> binaryTreePaths(TreeNode root) {
       List<String> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        List<Integer> paths = new ArrayList<>(); //作为结果中的路径
        traversal(root,paths,res);
        return res;
    }

    //前序遍历
    private void traversal(TreeNode root, List<Integer> paths, List<String> res) {
        paths.add(root.val);
        //遇到叶子节点
        if(root.left == null && root.right == null){
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < paths.size()-1; i++) {
                sb.append(paths.get(i)).append("->");
            }
            sb.append(paths.get(paths.size() -1)); //记录最后一个节点
            res.add(sb.toString()); //收集一个路径
            return;
        }
        // 递归和回溯是同时进行,所以要放在同一个花括号里
        if (root.left != null){
            traversal(root.left,paths,res);
            paths.remove(paths.size() - 1); //回溯
        }
        if (root.right != null){
            traversal(root.right,paths,res);
            paths.remove(paths.size() - 1); //回溯
        }
    }

    //迭代法
    public List<String> binaryTreePaths1(TreeNode root) {
        List<String> result = new ArrayList<>();
        if (root == null)return result;
        //因为要掉头,所以不用队列要用栈了
        Stack<Object> stack = new Stack<>();

        //节点和路径同时ru栈
        stack.push(root);
        stack.push(root.val + "");
        while (!stack.isEmpty()){
            String path = (String) stack.pop();
            TreeNode node = (TreeNode) stack.pop();
            //若找到叶子节点
            if (node.left == null && node.right == null){
                result.add(path);
            }
            //右子节点不为空
            if (node.right != null){
                stack.push(node.right);
                stack.push(path + "->" + node.right.val);
            }

            //左节点不为空
            if (node.left != null){
                stack.push(node.left);
                stack.push(path + "->" + node.left.val);
            }
        }
        return result;
    }
}

8、左叶子之和

思路:层序遍历,该层的第一个元素且是叶子节点。

public class Case404 {
    // 递归(后续遍历)
    //如何只让左叶子节点加和?????
    public int sumOfLeftLeaves(TreeNode root) {
       if (root == null)return 0;
        int a = sumOfLeftLeaves(root.left);
        int b = sumOfLeftLeaves(root.right);
        int midvaule = 0;
        if (root.left != null && root.left.left == null && root.left.right == null){
            midvaule = root.left.val;
        }
        int sum = midvaule + a + b;
        return sum;
    }

    //层序遍历
    public int sumOfLeftLeaves1(TreeNode root) {
        int sum = 0;
        if (root == null) return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (queue.isEmpty()){
            int len = queue.size();
            while (len-- > 0) {
                TreeNode node = queue.poll();
                if (node.left != null) {
                    if (node.left.left == null && node.left.right == null) {
                        sum += node.left.val;
                    }
                }
                if (node.right != null) queue.offer(node.right);
            }
        }
        return  sum;
    }

}

9、找树左下角的值

给定一个二叉树,在树的最后一行找到最左边的值

思路: 层序遍历,至最后一行,拿到第一个元素

public class Case513 {
    private int DEEP = -1;
    private int value = 0;

    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int res = 0;
        while (!queue.isEmpty()){
            int len = queue.size();
            //这样写会导致res的值最后会变成最后一个出队列元素的值,所以这样写循环是不对的
            /*while (len-- > 0){
                TreeNode p = queue.poll();
                res = p.val;
               if (p.left != null)queue.offer(p.left);
               if (p.right != null)queue.offer(p.right);

            }
        }*/
            for (int i = 0; i < len; i++) {
                TreeNode p = queue.poll();
                if (i == 0) res = p.val;
                if (p.left != null)queue.offer(p.left);
                if (p.right != null)queue.offer(p.right);
            }
        }
        return res;
    }

    /**
     * 递归做法:
     * 1.确定最大深度,找到最后一层 维护一个全局变量DEEP
     * 2.因为是先对左孩子进行递归处理,也即先访问的左孩子。
     * 3.维护一个全局变量vaule来记录结果
     */
    public int findBottomLeftValue1(TreeNode root) {
        value = root.val;
        findLeftVaule(root,0);
        return value;
    }

    private void findLeftVaule(TreeNode root, int deep) {
        if (root == null)return;
        if (root.left == null && root.right == null){
            if (deep > DEEP){
                value = root.val;
                DEEP = deep;
            }
        }
     if (root.left != null)findLeftVaule(root.left,deep + 1);
     if (root.right != null)findLeftVaule(root.right,deep + 1);
    }
}

10、路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。说明: 叶子节点是指没有子节点的节点。示例: 给定如下二叉树,以及目标和 sum = 22

 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

public class Case112 {


    public boolean hasPathSum(TreeNode root, int targetSum) {

        if (root == null) return false;
        Stack<Object> stack = new Stack<>();
        stack.push(root);
        stack.push(root.val);

        while (!stack.isEmpty()){
            int res = (int) stack.pop();
            TreeNode node = (TreeNode) stack.pop();

            if (node.left == null && node.right == null){
                if (res == targetSum)return true;
            }
            if (node.right !=null){
                stack.push(node.right);
                stack.push(res + node.right.val);
            }
            if (node.left !=null){
                stack.push(node.left);
                stack.push(res + node.left.val);
            }
        }
        return false;
    }
}

11、构造二叉树

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

注意: 你可以假设树中没有重复的元素。

例如,给出

  • 中序遍历 inorder = [9,3,15,20,7]
  • 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:

public class Case106 {
    private static Map<Integer,Integer> map;

    public static void main(String[] args) {
        int[] inorder = {9,3,15,20,7};
        int[] postorder = {9,15,7,20,3};
        buildTree(inorder,postorder);

    }

    public static TreeNode buildTree(int[] inorder, int[] postorder) {
        map = new HashMap<>();
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i],i);
        }
        return findNode(inorder,0,inorder.length,postorder,0,postorder.length);
    }

    private static TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {

        if (inBegin >= inEnd || postBegin >= postEnd){ //没有元素,返回空树,左闭右开[)
                return null;
        }
        int rootIndex = map.get(postorder[postEnd - 1]); //找到后续遍历的最后一个元素在中序遍历中的位置
        TreeNode root = new TreeNode(inorder[rootIndex]); //构造节点
        int lenOfLeft = rootIndex - inBegin; //保存中序左子树个数,用来确定后序数列的个数
        root.left = findNode(inorder,inBegin,rootIndex,postorder,postBegin,postBegin + lenOfLeft);
        root.right = findNode(inorder,rootIndex + 1,inEnd,postorder,postBegin+lenOfLeft,postEnd - 1);
        return root;
    }
}

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

public class Case105 {
    Map<Integer,Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i],i);
        }
        return findNode(inorder,0,inorder.length,preorder,0,preorder.length);
    }

    private TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] preorder, int preBegin, int preEnd) {

        if (inBegin >= inEnd || preBegin >= preEnd)return null;
        int rootIndex = map.get(preorder[preBegin]); //根节点在中序遍历中的索引
        TreeNode root = new TreeNode(inorder[rootIndex]);

        int lenOfLeft = rootIndex - inBegin;
        root.left = findNode(inorder,inBegin,rootIndex,preorder,preBegin + 1,preBegin + lenOfLeft + 1);
        root.right = findNode(inorder,rootIndex + 1,inEnd,preorder,preBegin+lenOfLeft + 1,preEnd);
        return root;
    }
}

11.3、最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

  • 二叉树的根是数组中的最大元素。
  • 左子树是通过数组中最大值左边部分构造出的最大二叉树。
  • 右子树是通过数组中最大值右边部分构造出的最大二叉树。

通过给定的数组构建最大二叉树,并且输出这个树的根节点。

/**
 * 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
 * 创建一个根节点,其值为 nums 中的最大值。
 * 递归地在最大值左边的子数组前缀上 构建左子树。
 * 递归地在最大值右边的子数组后缀上 构建右子树。
 * 返回 nums 构建的 最大二叉树
 */
public class Case654 {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if (nums == null)return null;
        return findNode(nums,0,nums.length);
    }

    private TreeNode findNode(int[] nums, int leftIndex, int rightIndex) {
        if (leftIndex >= rightIndex){  //数组不合法(左闭右开)
            return null;
        }
        int rootIndex = leftIndex;
        int max = nums[rootIndex];
        //max = Arrays.stream(nums).max().getAsInt(); //问题出在这,不应该在整个数组中找最大值了
        for (int i = leftIndex; i < rightIndex; i++) {
            if (nums[i] > max){
                max = nums[i];
                rootIndex = i;
            }
        }
        TreeNode root = new TreeNode(max);
        root.left = findNode(nums,leftIndex,rootIndex);
        root.right = findNode(nums,rootIndex + 1,rightIndex);
        return root;
    }
}

 12、合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

public class Case617 {

    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        /*if (root1 == null) return root2;
        if (root2 == null) return root1;*/
        if (root1 == null || root2 == null) {
            return root1 == null ? root2:root1;
        }
        root1.val += root2.val;
        root1.left = mergeTrees(root1.left,root2.left);
        root1.right = mergeTrees(root1.right,root2.right);
        return root1;
    }
}

 13、二叉搜索树

13.1 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

 public TreeNode searchBST(TreeNode root, int val) {
       /* if (root == null) return null;
        if (root.val == val) return root;
        TreeNode left = searchBST(root.left,val);
        if (left != null){
            return left;
        }
        return searchBST(root.right,val);*/

        //利用二叉搜索树的特点
        if (root == null || root.val == val) return root;
        if (root.val < val){
            return searchBST(root.right,val);
        }else {
            return searchBST(root.left,val);
        }
    }

 13.2、验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征:

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

/**
 * 验证二叉搜索树
 * 问题所在:不能局部判断,要保证左子树的全部节点都要比root小.......
 */
public class Case98 {
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
       /* if (root.left.val >= root.val || root.right.val <= root.val){
            return false;
            报空指针异常:如果左子树上来为空,那么问题就出现了
        }*/

        Boolean left = isValidBST(root.left);
        Boolean right = isValidBST(root.right);

        return  left && right;
    }

    //上述代码问题就出现在全局问题!!!
    public boolean isValidBST1(TreeNode root ){
        return isValidBSTHelper(root,Long.MIN_VALUE,Long.MAX_VALUE);
    }
    private boolean isValidBSTHelper(TreeNode root, long minValue, long maxValue) {
        if (root == null) return true;
        if (root.val <= minValue || root.val >= maxValue){
            return false;
        }
        //确定范围:左孩子一定要比根节点的值小,右孩子一定要比根节点的值要大
        return isValidBSTHelper(root.left,minValue, root.val) &&
                isValidBSTHelper(root.right, root.val, maxValue);
    }

    //迭代法
    public boolean isValidBST2(TreeNode root ){
        if (root == null) return true;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode pre = null; //记录父节点
        while (root != null || !stack.isEmpty()){
            while (root != null){
                stack.push(root);
                root = root.left;
            }
            TreeNode pop = stack.pop();
            if (pre!=null && pop.val <= pre.val){
                return false;
            }
            pre = pop;
            root = root.right;
        }

        return true;
    }
}

13.3、二叉搜索树的最小绝对差

给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。

public class Case530 {
    int res = Integer.MAX_VALUE;
    TreeNode pre;  //记录上一个遍历的节点

    public int getMinimumDifference(TreeNode root) {
        func(root);
        return res;
    }

    private void func(TreeNode root) {
        if (root == null) return;
        func(root.left);
        if (pre != null) {
            res = Math.min(res, root.val - pre.val);
        }
        pre = root;
        func(root.right);
    }

    //迭代法-中序遍历
    public int getMinimumDifference1(TreeNode root) {
        if (root == null) return 0;
        TreeNode pre = null;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        int res = Integer.MAX_VALUE;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur); //将要访问的节点放进栈
                cur = cur.left;
            } else {
                cur = stack.pop();
                if (pre != null) { //中
                    res = Math.min(res, cur.val - pre.val);
                }
                pre = cur;
                cur = cur.right; //右
            }
        }
        return res;
    }
}

13.4 二叉搜索树中的众数

给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。结果集合中可以是多个元素。

思路:定义全局变量 count 记录节点值出现的次数,maxCount记录出现次数最多的节点值,

public class Case501 {
    //迭代遍历
    public int[] findMode(TreeNode root) {
        if(root == null)return null;
        List<Integer> res = new ArrayList<>();
        int maxCount = 0;
        int count = 0;
        TreeNode pre = null;
        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){
                   count = 1;
                }else {
                    count ++;
                }
                //更新结果
                if (count > maxCount){
                    maxCount = count;
                    res.clear();   //重新清空集合
                    res.add(cur.val);
                }else if (count == maxCount){
                    res.add(cur.val);
                }
                pre = cur;
                cur = cur.right;
            }
        }
        int[] a = new int[res.size()];
        for (int i = 0; i < res.size(); i++) {
            a[i] = res.get(i);
        }
        return a;
        //return res.stream().mapToInt(Integer:: intValue).toArray();
    }
}

13.5 二叉树的最近公共祖先

 

public class Case236 {
    /**
     * 1.求最小公共祖先,需要从底向上遍历,二叉树,只能通过后序遍历,实现从底向上的遍历方式
     * 2.在回溯过程中,必然要遍历整颗二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的
     * 返回值
     * 3.要理解如果返回值为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
     * 可以说这里每一步,都是有难度的,都需要对二叉树,递归和回溯有一定的理解。
     *本题没有给出迭代法,因为迭代法不适合模拟回溯的过程。理解递归的解法就够了。
     */

    //写得太牛逼了!!!! 值得反复揣摩。优雅
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q){ //强啊
            return root;
        }
        //后序遍历
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        if (left == null && right == null){ //如果未找到节点p或q
            return null;
        }else if (left == null && right != null){ //若找到一个节点
            return right;
        }else if (left != null && right == null){
            return left;
        }else { // 若找到两个节点
            return root;
        }
    }
}

13.6  二叉搜索树的最近公共祖先

public class Case235 {

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;
// 如果一个节点比root大,一个比root小,那就最近祖先就是root了啊
        while (root != null){
            if (root.val > p.val && root.val > q.val){
                root = root.left;
            }else if (root.val < p.val && root.val < q.val){
                root = root.right;
            }else {
                break;
            }
        }
        return root;
    }

    //递归
    public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val > p.val && root.val > q.val){
            return lowestCommonAncestor(root.left,p,q);
        }
        if (root.val < p.val && root.val < q.val){
            return lowestCommonAncestor(root.right,p,q);
        }
        return root;
    }
}

13.7 二叉搜索树中的插入操作

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据保证,新值和原始二叉搜索树中的任意节点值都不同。注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。

public class Case701 {

    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null){
            TreeNode p = new TreeNode(val);
            return p;
        }
        if (root.val > val){
            root.left = insertIntoBST(root.left,val);
        }else {
            root.right = insertIntoBST(root.right,val);
        }
        return root;
    }
    //非递归
    public TreeNode insertIntoBST1(TreeNode root, int val) {
        if (root == null) return new TreeNode(val);
        TreeNode newRoot = root;
        TreeNode pre = root;
        while (root != null){
            pre = root;
            if (root.val > val){
                root = root.left;
            }else if (root.val < val){
                root = root.right;
            }
        }
        if (pre.val > val){
            pre.left = new TreeNode(val);
        }else {
            pre.right = new TreeNode(val);
        }
        return newRoot;
    }
}

13.8 删除二叉搜索树中的节点

class Solution {
  public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return root;
    if (root.val == key) {
      if (root.left == null) {
        return root.right;
      } else if (root.right == null) {
        return root.left;
      } else {
        TreeNode cur = root.right;
        while (cur.left != null) {
          cur = cur.left;
        }
        cur.left = root.left;
        root = root.right;
        return root;
      }
    }
    if (root.val > key) root.left = deleteNode(root.left, key);
    if (root.val < key) root.right = deleteNode(root.right, key);
    return root;
  }
}

13.9 修剪二叉搜索树

给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

public class Case669 {

    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null || low > high) return root;
        //如果根节点就不在合法范围内,那就先找到第一个合法的节点
        while (root != null && (root.val < low || root.val > high)){
            if (root.val < low){
                root = root.right;
            }else {
                root = root.left;
            }
        }
        TreeNode curr = root;
        //处理左子树
        while (curr != null){
            while (curr.left != null && curr.left.val < low){
                curr.left = curr.left.right;
            }
            curr = curr.left;
        }
        curr = root;  //重新回到根节点

        //处理右子树
        while (curr != null){
            while (curr.right != null && curr.right.val > high){
                curr.right = curr.right.left;
            }
            curr = curr.right;
        }
        return root;
    }

    //递归
    public TreeNode trimBST1(TreeNode root, int low, int high) {

        /*if (root == null || root.val < low || root.val > high) return null;
        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);
        return root;*/
        if (root == null){
            return null;
        }

        if (root.val < low){
            return trimBST1(root.right,low,high);
        }
        if (root.val > high){
            TreeNode a = trimBST1(root.left,low,high);
            return a;
        }
        //root 在合法的范围内
        root.left = trimBST1(root.left,low,high);
        root.right = trimBST1(root.right,low,high);
        return root;
    }
}

 13.10 将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

public TreeNode sortedArrayToBST(int[] nums) {
        if (nums == null) return null;
        return sortedArrayToBST(nums,0,nums.length);
    }

    private TreeNode sortedArrayToBST(int[] nums, int left, int right) {
        if (left >= right){  //要求左闭右开
            return null;
        }
        int mid = (left + right) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = sortedArrayToBST(nums,left,mid);
        root.right = sortedArrayToBST(nums,mid + 1,right);
        return root;
    }
}

13.11 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。提醒一下,二叉搜索树满足下列约束条件:节点的左子树仅包含键 小于 节点键的节点。 节点的右子树仅包含键 大于 节点键的节点。 左右子树也必须是二叉搜索树。

public class Case538 {
  private  int num = 0;
    public TreeNode convertBST(TreeNode root) {
        if (root == null)return null;

        /*root.right = convertBST(root.right);
        if (root.right != null){
            root.val = root.val + root.right.val;
        }
        root.left = convertBST(root.left);
        if (root.left != null){
            root.left.val = root.val + root.left.val;
        }
        return root;*/
        //上述代码虽然遵从了逆中序遍历,但是由于递归,
        convertBST(root.right);
        root.val = root.val + num;
        num = root.val;
        convertBST(root.left);
        return root;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值