代码随想录自刷07:二叉树

58 阅读20分钟

102. 二叉树的层序遍历

102. 二叉树的层序遍历

思路:使用queue来辅助实现,queue时先进先出,所以符合我们要的层序顺序

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list=new ArrayList<>();
        if(root==null)
            return list;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            List<Integer> path=new ArrayList<>();
            while(size>0){
                TreeNode cur=queue.poll();
                path.add(cur.val);
                if(cur.left!=null)
                    queue.add(cur.left);
                if(cur.right!=null)
                    queue.add(cur.right);
                size--;
            }
            list.add(new ArrayList<>(path));
        }
        return list;
    }

107. 二叉树的层序遍历 II

107. 二叉树的层序遍历 II

思路:跟上面一题类似,只是结果要反过来。可以多用一个List(reverse)暂存结果,然后最后用for倒序取出该List中的元素放到最终要返回的集合List(res)中。

public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        if(root==null)
            return res;
        queue.add(root);
        List<List<Integer>> reverse=new ArrayList<>();
        while(!queue.isEmpty()){
            int size=queue.size();
            List<Integer> list=new ArrayList<>();
            while(size>0){
                TreeNode cur=queue.poll();
                list.add(cur.val);
                if(cur.left!=null)
                    queue.add(cur.left);
                if(cur.right!=null)
                    queue.add(cur.right);
                size--;
            }
            reverse.add(list);
        }
        //倒序遍历,取出元素
        for(int i=reverse.size()-1;i>=0;i--){
            res.add(reverse.get(i));
        }
        return res;
    }

199. 二叉树的右视图

199. 二叉树的右视图

思路:注意,可能右子树为空,左子树有值,此时站在右边看的话看到的值是左子树的哦!其实就是获取每层queue中最后一个元素就是离我们最近的值!也就是当size=1时,我们要将结果加入集合中!

public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        if(root==null)
            return res;
        queue.add(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            while(size>0){
                TreeNode cur=queue.poll();
                if(cur.left!=null)
                    queue.add(cur.left);
                if(cur.right!=null)
                    queue.add(cur.right);
                //判断size是否为1
                if(size==1)
                    res.add(cur.val);
                size--;
            }
        }
        return res;
    }

637. 二叉树的层平均值

637. 二叉树的层平均值

思路:跟前面其实差不多,只是现在变成获取每一层的平均值,所以要记录每一层个数(len),然后总和(sum)。在每一层结束时进行平均值计算,然后加入集合中

public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        if(root==null)
            return res;
        queue.add(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            int len=size;
            Double sum=0.0;
            while(size>0){
                TreeNode cur=queue.poll();
                sum+=cur.val;
                if(cur.left!=null)
                    queue.add(cur.left);
                if(cur.right!=null)
                    queue.add(cur.right);
                size--;
            }
            res.add(sum/len);
        }
        return res;
    }

思路:注意Node的属性为:

  • int val
  • List< Node > children

现在是n叉树,所以在把当前节点的子节点加入queue时,不能单纯用left和right了,要根据children的大小来遍历子节点!

public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> result=new ArrayList<>();
        if(root==null)
            return result;
        Queue<Node> queue=new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> list=new ArrayList<>();
            int size=queue.size();
            while(size>0){
                Node node=queue.poll();
                //注意:获得存放子节点集合的大小,得知有几个子节点
                int childSize=node.children.size();
                list.add(node.val);
                for(int i=0;i<childSize;i++){
                    if(node.children.get(i)!=null)
                        queue.add(node.children.get(i));
                }
               size--;
            }
            result.add(list);
        }
        return result;

    }

515. 在每个树行中找最大值

515. 在每个树行中找最大值

思路:思路就不多说了,跟前面差不多!

public List<Integer> largestValues(TreeNode root) {
        List<Integer> res=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        if(root==null)
            return res;
        queue.add(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            int max=Integer.MIN_VALUE;
            while(size>0){
                TreeNode cur=queue.poll();
                if(cur.left!=null)
                    queue.add(cur.left);
                if(cur.right!=null)
                    queue.add(cur.right);
                max=Math.max(cur.val,max);
                size--;
            }
            res.add(max);
        }
        return res;

116. 填充每个节点的下一个右侧节点指针(提供两种解答)

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

思路:可以使用层次或递归的方式解决这题!

层次遍历:大部分步骤都是跟之前一样的,只是现在每一层要进行遍历时,需要有一个节点pre记录上一个节点!(在while循环外面声明pre。)

  • 每一层开始的第一个节点,此时pre是null的所以不会进行操作
  • 之后从queue中获取到的每一个节点,pre的next指针会指向当前节点!
  • 将当前节点的子节点加入queue中
  • 然后pre=当前节点

层次遍历:

public Node connect(Node root) {
        Queue<Node> queue=new LinkedList<>();
        if(root==null)
            return root;
        Node node=root;
        queue.add(node);
        while(!queue.isEmpty()){
            int size=queue.size();
            Node pre=null;
            while(size>0){
                Node cur=queue.poll();
                if(pre!=null){
                    pre.next=cur;
                }
                if(cur.left!=null)
                    queue.add(cur.left);
                if(cur.right!=null)
                    queue.add(cur.right);
                pre=cur;
                size--;
            }
        }
        return root;
    }

思路:递归的思路是怎样的呢?稍微有点抽象。首先了解:完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点

  • 先处理当前节点(cur)
    • 左节点next处理:如果cur.left不为null(cur一定有2子节点),则将左节点的next指向cur.right
    • 右节点next处理:如果cur.next不为null,表示有节点(根据题目,该节点也一定有2子节点才能确保叶子节点都在同一层)!将next指向当cur.next.left
  • 处理左节点
  • 处理右节点

递归:

public Node connect(Node root) {
        if(root==null)
            return root;
        if(root.left!=null){
            //当前节点的左边连起来
            root.left.next=root.right;
            //当前节点的右边尝试连起来
            if(root.next!=null){
                root.right.next=root.next.left;
            }
        }
        
        connect(root.left);
        connect(root.right);
        return root;
    }

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

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

思路:这题跟上面116的区别在于!不是完美二叉树,所以叶子节点不一定都在同一层!父节点也不一定有2个子节点!

所以判断条件变多了,用递归处理比较方便。并且注意⚠️:这里要先递归右边!!而不是先左边!因为要把next给连接好!

public Node connect(Node root) {
        if(root==null)
            return root;
        //设置左节点的next指针
        if(root.left!=null && root.right!=null)
            root.left.next=root.right;
        else if(root.left!=null && root.right==null)
            root.left.next=getNext(root.next);
        //设置右节点的next指针
        if(root.right!=null)
            root.right.next=getNext(root.next);
        //把右边的next关系都搞定好
        connect(root.right);
        connect(root.left);
        return root;
    }
    //找离我最近的那个节点!
    public Node getNext(Node root){
        if(root==null)
            return root;
        if(root.left!=null)
            return root.left;
        if(root.right!=null)
            return root.right;
        if(root.next!=null)
            return getNext(root.next);
        return null;
    }

226. 翻转二叉树

226. 翻转二叉树

思路:用递归实现的。

public TreeNode invertTree(TreeNode root) {
        //递归终止条件
        if(root==null)
            return root;
        //准备交换    
        TreeNode tmp=root.left;
        //递归得到交换好的节点,赋值给左节点
        root.left=invertTree(root.right);
        //递归得到交换好的节点,赋值给右节点
        root.right=invertTree(tmp);
        return root;
    }

101. 对称二叉树

101. 对称二叉树

思路:对称判断,如果当前两个节点相同才继续往下比较:是对称比较所以要注意不是每个节点的右子节点or左子节点进行比较!

    public boolean isSymmetric(TreeNode root) {
        if(root==null)
            return true;
        return backTraversal(root.left,root.right);
    }
    public boolean backTraversal(TreeNode node1,TreeNode node2){
        if(node1==null && node2==null)
            return true;
        if(node1!=null && node2==null)
            return false;
        if(node1==null && node2!=null)
            return false;
        if(node1.val!=node2.val)
            return false;
        //注意选取的节点一个是left一个是right
        boolean b1=backTraversal(node1.left,node2.right);
        boolean b2=backTraversal(node1.right,node2.left);
        return b1&&b2;
    }

104. 二叉树的最大深度

104. 二叉树的最大深度

思路:

  • 二叉树节点的深度:从根节点到该节点的最长简单路径边的条数或者节点数
  • 二叉树节点的高度:从该节点到叶子节点的最长简单路径边的条数或者节点数
  • 使用前序求的就是深度,使用后序求的是高度。

使用后序,并且求的范围是从根节点到叶子节点的高度,其实就是求二叉树的最大深度(根节点到叶子节点)

public int maxDepth(TreeNode root) {
        if(root==null)
            return 0;
        int left=maxDepth(root.left);
        int right=maxDepth(root.right);
        //+1是加上当前节点的高度
        return Math.max(left,right)+1;
    }

559. N 叉树的最大深度

559. N 叉树的最大深度

思路:也是求最大深度的,但它是n叉节点!所以在获取子节点传回来的高度时要注意一下获取方式会不同

public int maxDepth(Node root) {
        if(root==null)
            return 0;
        int max=0;
        for(Node child : root.children){
            //递归子节点
            max=Math.max(max,maxDepth(child));
        }
        //得到从子节点传回来的高度,现在要加上当前节点的高度
        return max+1;
    }

111. 二叉树的最小深度

111. 二叉树的最小深度

思路:最小深度是从根节点到最近叶子节点的最短路径上的节点数量,这题如果用后序遍历,要注意有陷阱,不能是上面的写法直接将Math.max改为min!这样就中招了。因为找到的不是叶子节点

看清题目,是到叶子节点:没有左右子节点的才叫叶子节点!

public int minDepth(TreeNode root) {
        //终止条件
        if(root==null)
            return 0;
        //左
        int left=minDepth(root.left);
        //右
        int right=minDepth(root.right);
        //中
        // 当一个左子树为空,右不为空,这时的节点并不是叶子节点
        if(root.left==null && root.right!=null)
            return right+1;
        // 当一个右子树为空,左不为空,这时的节点并不是叶子节点
        if(root.left!=null && root.right==null)
            return left+1;
        //左子树和右子树都为空,表示的节点为叶子节点。
        return Math.min(left,right)+1;
    }

简化一下:

public int minDepth(TreeNode root) {
        //终止条件
        if(root==null)
            return 0;
        //左
        int left=minDepth(root.left);
        //右
        int right=minDepth(root.right);
        //中
        // 当一个左子树为空,右不为空(可能为空但不重要),这时的节点并不是(最低点)叶子节点
        if(root.left==null)
            return right+1;
        // 当一个右子树为空,左不为空,这时的节点并不是(最低点)叶子节点
        if(root.right==null)
            return left+1;
        //左子树和右子树都不为空,表示的节点不是(最低点)叶子节点
        return Math.min(left,right)+1;
    }

222. 完全二叉树的节点个数

222. 完全二叉树的节点个数

思路:求节点数,也是要遍历所有节点,所以其实跟求深度差不多的写法,只是return时不一样:

  • 深度是只获取一边的结果
  • 而节点个数是两边都要!这样才能计算所有节点个数
public int countNodes(TreeNode root) {
        if(root==null)
            return 0;
        int left=countNodes(root.left);
        int right=countNodes(root.right);
        //加上当前节点
        return left+right+1;
    }

110. 平衡二叉树

110. 平衡二叉树

思路:根据题意,是每个节点的左右子树高度差不超过1,所以用递归处理。同时需要一个计算高度的方法。

public boolean isBalanced(TreeNode root) {
        if(root==null)
            return true;
        if(Math.abs(getHeight(root.left)-getHeight(root.right))>1)
            return false;
        return isBalanced(root.left)&& isBalanced(root.right);
    }
    //计算高度
    public int getHeight(TreeNode root){
        if(root==null) 
            return 0;
        int left=getHeight(root.left);
        int right=getHeight(root.right);
        return Math.max(left,right)+1;
    }

257. 二叉树的所有路径(回溯)

257. 二叉树的所有路径

思路:按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。 回溯思想!回溯:递归+回退。

  • 需要有一个集合暂时记录目前走过的节点,用list集合,名字叫path
  • 经过的每一个节点都要记录起来所以一开始就path.add
  • 判断是否找到符合的结果之一,题目要求走到叶子节点才算终止,所以判断条件就是当前节点是否为叶子节点!如果是表示走完一条路径可以加入最终集合了!
  • 进行递归+回退,这里会判断下一个要走的节点是否为null,如果不为null才会进行递归!所以一开始加入节点时,path不会加入null
List<String> list;
    List<Integer> path;
    public List<String> binaryTreePaths(TreeNode root) {
        list=new ArrayList<>();
        path=new ArrayList<>();
        backTrack(root,list,path);
        return list;
    }
    public void backTrack(TreeNode root,List<String> list,List<Integer> path){
        path.add(root.val);
        if(root.left==null && root.right==null){
            StringBuilder sb=new StringBuilder();
            for(int i=0;i<path.size()-1;i++){
                sb.append(path.get(i));
                sb.append("->");
            }
            sb.append(path.get(path.size()-1));
            list.add(sb.toString());
            return;
        }
        //这里会过滤掉节点为null的情况
        if(root.left!=null){
            backTrack(root.left,list,path);
            path.remove(path.size()-1);
        }
        //这里会过滤掉节点为null的情况
        if(root.right!=null){
            backTrack(root.right,list,path);
            path.remove(path.size()-1);
        }
    
    }

404. 左叶子之和

404. 左叶子之和

思路:获取每一个左叶子的值

  • 先判断当前节点.left如果不为空时表示有可能为左叶子
  • 进一步判断:当前节点.left.left 当前节点.left.right 都为空时,当前节点.left就是我们要找的左叶子!
public int sumOfLeftLeaves(TreeNode root) {
        if(root==null)
            return 0;
        int leftSum=0;
        leftSum+=sumOfLeftLeaves(root.left);
        leftSum+=sumOfLeftLeaves(root.right);
        if(root.left!=null && root.left.left==null && root.left.right==null)
            leftSum+=root.left.val;
        return leftSum;
    }

513. 找树左下角的值

513. 找树左下角的值

思路:要找最后一行,最左边的节点。提供递归版和层序版。 递归版:

  • 找最后一行就是找到最大深度,那么如何找到最左边的节点,那就是先从左边找起来。
  • 每次来到最新的当前最高深度时,此时只会记录第一个遇到的节点(如果我们先从左边找,那记录到的就是当前最左边)
int leftSum=0;
    int maxDep=0;
    public int findBottomLeftValue(TreeNode root) {
        if(root==null)
            return leftSum;
        backTrack(root,1);
        return leftSum;
    }   
    public void backTrack(TreeNode root,int curDep){
        if(root.left==null && root.right==null){
            //第一次来到目前最深层(最底层)
            if(maxDep<curDep){
                //记录一个节点就是最左边
                leftSum=root.val;
                maxDep=curDep;
            }
            return;
        }
        //先从左边找起!这样记录到的第一个leftSum才是最左边
        if(root.left!=null)
            backTrack(root.left,curDep+1);
        if(root.right!=null)
            backTrack(root.right,curDep+1);
    }

层序遍历版:

  • 因为是一层层遍历的,所以记录每一层第一个节点!这样到最后一行时,就自然记录到了最后一行、最左边的节点
public int findBottomLeftValue(TreeNode root) {
        if(root==null)
            return 0;
        int leftNum=0;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode node=queue.peek();
            //记录新一层的第一个节点
            leftNum=node.val;
            int size=queue.size();
            while(size>0){
                node=queue.poll();
                if(node.left!=null)
                    queue.add(node.left);
                if(node.right!=null)
                    queue.add(node.right);
                size--;
            }
        }
        return leftNum;
    }

112. 路径总和

112. 路径总和

思路:涉及到回溯,但这里跟之前不一样,有返回值!因为我只要找到一个答案即可返回!不需要遍历所有

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null)
            return false;
        return backTrack(root,targetSum);
    }
    public boolean backTrack(TreeNode root, int targetSum){
        if(root.left==null && root.right==null){
            if(root.val==targetSum){
                return true;
            }
            return false;
        }
        if(root.left!=null){
            if(hasPathSum(root.left,targetSum-root.val))
                return true;
        }
        if(root.right!=null){
            if(hasPathSum(root.right,targetSum-root.val))
                return true;
        }
        return false;
    }

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

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

思路:根据中序(中左右)和后序(左右中)的规律,可得知中间节点的位置!

  • 可以得知在后序时,中间点都是最后一个,中序时先左边再中节点
  • 所以通过for循环遍历中序,每个元素跟后序最后一个元素(中节点)比较,相同表示在中序中找到中节点的位置,记录下标跳出
  • 创建中节点root
  • 递归找出左右节点
    • 注意⚠️:这里用到了Arrays.CopyOfRange(数组,起始位置,结束位置),实际范围不含结束位置
public TreeNode buildTree(int[] inorder, int[] postorder) {
        //终止条件
        if(inorder.length==0 || postorder.length==0)
            return null;
        //left mid right
        //left right mid
        int index=0;
        for(int i=0;i<inorder.length;i++){
            if(inorder[i]==postorder[postorder.length-1]){
                index=i;
                break;
            }
        }
        TreeNode root=new TreeNode(postorder[postorder.length-1]);
        root.left=buildTree(Arrays.copyOfRange(inorder,0,index),Arrays.copyOfRange(postorder,0,index));
        root.right=buildTree(Arrays.copyOfRange(inorder,index+1,inorder.length),Arrays.copyOfRange(postorder,index,postorder.length-1));
        return root;
    }

这一题跟他很像,步骤差不多只需要根据中节点位置判断即可:105. 从前序与中序遍历序列构造二叉树

654. 最大二叉树

654. 最大二叉树

思路:这题其实也一样,只是变成,每个数组中最大的值才是中节点,以中节点为分界分出左边树和右边数。每个树也是按照上面的逻辑来找出各自的中节点。

public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums.length==0)
            return null;
        int idx=0;
        int max=nums[0];
        for(int i=1;i<nums.length;i++){
            if(nums[i]>max){
                idx=i;
                max=nums[i];
            }
        }
        TreeNode root=new TreeNode(nums[idx]);
        root.left=constructMaximumBinaryTree(Arrays.copyOfRange(nums,0,idx));
        root.right=constructMaximumBinaryTree(Arrays.copyOfRange(nums,idx+1,nums.length));
        return root;
    }

617. 合并二叉树

617. 合并二叉树

思路:先判断如果有一个节点为null,则返回另一个节点即可。否则表示两个节点都有值,要进行相加。

  • 先处理中节点
  • 再处理左右
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1==null)
            return root2;
        if(root2==null)
            return root1;
        TreeNode root=new TreeNode(root1.val+root2.val);
        root.left=mergeTrees(root1.left,root2.left);
        root.right=mergeTrees(root1.right,root2.right);
        return root;
    }

700. 二叉搜索树中的搜索

700. 二叉搜索树中的搜索

思路:二叉搜索树,有一个特性,左边节点<中节点<右边节点。节点的左边子树恒小于中节点,右子树恒大于该节点。

所以要有目的的去选择往哪边搜寻!如果val大于中节点则往右找,反之往左找。

 public TreeNode searchBST(TreeNode root, int val) {
        //终止条件
        if(root==null)
            return root;
        //往哪边遍历
        if(root.val<val)
            return searchBST(root.right,val);
        if(root.val>val)
            return searchBST(root.left,val);
        //找到val,返回节点
        else
            return root;
    }

98. 验证二叉搜索树

98. 验证二叉搜索树

思路:这题要明白树的特性(上一题有讲到:左边节点<中节点<右边节点)。

因此用中序遍历,可以得到一个升序的数组,通过中序遍历来判断每个节点是否符合二叉搜索树的特性。

这里注意,需要一个节点记录上一个节点pre

TreeNode pre=null;
    public boolean isValidBST(TreeNode root) {
        if(root==null)
            return true;
        return backTraversal(root);

    }
    public boolean backTraversal(TreeNode root){
        //终止条件
        if(root==null)
            return true;
        //左
        boolean bl= backTraversal(root.left);
        //中
        if(pre!=null){
            if(pre.val>=root.val){
                return false;
            }
        }
        pre=root;
        //右
        boolean br=backTraversal(root.right);
        return bl && br;
    }

530. 二叉搜索树的最小绝对差

530. 二叉搜索树的最小绝对差

思路:再复习一遍特性:左边节点<中节点<右边节点,用中序遍历可以得到升序数组。

这里要求最小绝对差,那肯定是从数字相邻的两个数可以得到最小差,升序数组就是符合这样的条件。所以用中序遍历!

class Solution {
    TreeNode pre=null;
    int result=Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        backTraversal(root);
        return result;
    }
    public void backTraversal(TreeNode root){
        //终止条件
        if(root==null)
            return;
        //左
        backTraversal(root.left);
        //中
        if(pre!=null){
            result=Math.min(result,root.val-pre.val);
        }
        pre=root;
        //右
        backTraversal(root.right);

    }
}

501. 二叉搜索树中的众数

501. 二叉搜索树中的众数

思路:要求的是众数而且有可能有多个,所以需要一个集合来存储结果,创建时还不确定有几个结果,所以先用list来存放,最后再转换成数组。

此外还需要一个变量count记录:当前数字出现的数量、maxCount记录:遍历过程中遇到的最大众数(相同数字出现的数量,最大是出现几次)

因为二叉搜索树的特性,所以有相同值的话,肯定是相连的,所以用中序遍历! 中节点的处理要注意下:

  • 需要判断当前数字是否和上一个节点相同,如果相同则count+1,否则count重置为1
  • 判断count和maxCount,如果相同则list加入当前数字,否则清空list然后加入当前数字,然后更新maxCount
class Solution {
    TreeNode pre=null;
    int count=0;
    int maxCount=0;
    List<Integer> list=new ArrayList<>();
    public int[] findMode(TreeNode root) {
        if(root==null)
            return new int[0];
        backTraversal(root);
        return list.stream().mapToInt(Integer::intValue).toArray();
    }
    public void backTraversal(TreeNode root){
        if(root==null)
            return;
        //左
        backTraversal(root.left);
        //中
        if(pre==null || root.val!=pre.val)
            count=1;
        else if(pre.val==root.val)
            count++;
        if(count==maxCount)
            list.add(root.val);
        else if(count>maxCount){
            list.clear();
            list.add(root.val);
            maxCount=count;
        }
        pre=root;
        //右
        backTraversal(root.right);
    }
}

236. 二叉树的最近公共祖先

236. 二叉树的最近公共祖先

思路:要找到pq的公共祖先,找到即可返回,所以这里的backTraversal方法要有返回值!

既然要找到这两个节点的共同祖先,则需要先找到这两个节点在哪里,一找到就返回该节点给递归上一层,交给中间节点处理。

所以这里要使用后序遍历,中间节点要进行判断处理:

  • 如果左右节点都不为空,表示找到两个节点,此时的中节点就是他们的最近祖先!
  • 左节点为空,右节点不为空,表示只找到一个节点,返回右节点
  • 反之返回左节点
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return backTraversal(root,p,q);
    }
    public TreeNode backTraversal(TreeNode root,TreeNode p,TreeNode q){
        if(root==null || root==p || root==q){
            return root;
        }
        //左
        TreeNode left=backTraversal(root.left,p,q);
        //右
        TreeNode right=backTraversal(root.right,p,q);
        //中
        if(left!=null && right!=null){
            return root;
        }
        if(left==null && right!=null){
            return right;
        }
        else
            return left;
    }
}

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

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

思路:二叉搜索树是有序的,所以要找到两个节点的最近祖先,它一定是介于两个节点之间的值!因此要往哪边找祖先,可以通过比较大小得知:

  • 如果两个节点均小于当前节点,则表示祖先要往左边去找。
  • 如果两个节点均大于当前节点,则表示祖先要往右边去找。
  • 如果不符合上述判断则表示当前节点是介于两个节点之间!当前节点就是要找的祖先!
  • 记得要有终止条件
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //终止条件
        if(root==null)
            return root;
        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);
        }
        else
            return root;
    }
}

701. 二叉搜索树中的插入操作

701. 二叉搜索树中的插入操作

思路:注意因为要插入节点,所以已有节点要衔接住这个新节点!题目说可以随便插入,所以在符合二叉搜索树的特性下,将新节点加入到树的末尾即可!

  • 如果当前节点大于要插入节点值,则新节点肯定是插入到当前节点的左边,由于插入了元素,所以要记得衔接住插入的元素。
  • 反之,插入到当前节点的右边,
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        return backTraversal(root,val);
    }
    public TreeNode backTraversal(TreeNode root,int val){
        if(root==null){
            //创建新节点
            return new TreeNode(val);
        }
        if(root.val>val){
            //衔接新节点
            root.left=backTraversal(root.left,val);
        }
        else if(root.val<val){
            //衔接新节点
            root.right=backTraversal(root.right,val);
        }
        //最终返回插入好节点的树
        return root;
    }
}

思路:

450. 删除二叉搜索树中的节点

450. 删除二叉搜索树中的节点

思路:跟上一题插入元素一样类似的步骤,节点依旧需要衔接住修改后的节点。

找到要删除的节点时需要注意!删除的当前节点可能:

  • 拥有左和右两个子节点!此时需要返回新的中节点(从左右子节点中选一个替补上去),这里选择由右子节点补上去作为新的中间节点,原来的左子节点则衔接在新节点的右边最下方即可!
  • 只拥有左子节点,则返回它即可
  • 只拥有右子节点,返回它即可
  • 没有子节点,返回null即可
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        return backTraversal(root,key);
    }
    public TreeNode backTraversal(TreeNode root,int key){
        //终止条件
        if(root==null)
            return root;
        //中
        if(root.val==key){
            //left && right==null
            if(root.left==null && root.right==null)
                return null;
            //left==null right!=null
            else if(root.left==null && root.right!=null)
                return root.right;
            //left!=null right==null
            else if(root.left!=null && root.right==null)   
                return root.left;
            //left && right !=null
            else{
                TreeNode preLeft=root.left;
                root=root.right;
                TreeNode node=root;
                while(node.left!=null){
                    node=node.left;
                }
                node.left=preLeft;
                return root;
            }
        }
        //左
        if(root.val>key){
            root.left=backTraversal(root.left,key);
        }
        //右
        else if(root.val<key)
            root.right=backTraversal(root.right,key);
            
        return root;
    }
}

669. 修剪二叉搜索树

669. 修剪二叉搜索树

思路:二叉搜索树的特性,所以可以根据当前节点是超过最大/最小边界来判断要返回哪边的节点合适!

如何处理当前节点:

  • 当前节点小于最小边界,表示该节点和左子节点均不符合边界要求,所以要往右子节点找符合边界要求的节点返回
  • 当前节点大于最大边界,表示该节点和右子节点均不符合边界要求,所以要往左子节点找符合边界要求的节点返回
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        return backTraversal(root,low,high);
    }
    public TreeNode backTraversal(TreeNode root,int low,int high){
        if(root==null)
            return root;
        //中
        //表示当前节点的左边均不符合最小边界的要求,所以要往右边找,才能不超过区间
        if(root.val<low){
            return backTraversal(root.right,low,high);
        }
        //表示当前节点的右边均不符合最大边界的要求,所以要往左边找,才能不超过区间
        if(root.val>high){
            return backTraversal(root.left,low,high);
        }
        //左
        root.left=backTraversal(root.left,low,high);
        //右
        root.right=backTraversal(root.right,low,high);
        
        return root;
    }
}

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

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

思路:每一次都是取数组中间的值,作为中节点。数组左边所有值为节点的左子树所有值,右边为右子树所有值。依此类推生成每一个节点,直到数组为空!

因为二叉搜索树特性左节点<中节点<右节点,所以取数组中间的值后,其左边恒小于它,右边恒大于它!符合该特性。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if(nums.length==0)
            return null;
        int idx=nums.length/2;
        TreeNode root=new TreeNode(nums[idx]);
        root.left=sortedArrayToBST(Arrays.copyOfRange(nums,0,idx));
        root.right=sortedArrayToBST(Arrays.copyOfRange(nums,idx+1,nums.length));
        return root;

    }
    
}

538. 把二叉搜索树转换为累加树

538. 把二叉搜索树转换为累加树

思路:这题没什么难度,搞清楚遍历顺序就行,如何遍历?根据累加顺序:发现是先加右节点,再加中节点,再加左节点。所以能得知遍历顺序应该是右中左!

class Solution {
    TreeNode pre=null;
    public TreeNode convertBST(TreeNode root) {
        if(root==null)
            return root;
        //右
        convertBST(root.right);
        //中
        if(pre!=null){
            root.val+=pre.val;
        }
        pre=root;
        //左
        convertBST(root.left);
        return root;
    }
}