二叉树常见问题

二叉树的遍历

包括前中后续的遍历方法,属于深度遍历,基于栈来进行实现,递归法比较简单这里不在叙述,主要讲的是迭代的思路。
比如说前序遍历,遵循根左右的顺序,那么入栈的时候就是右左根进行入栈,才能保证出栈是正确的顺序,这里参考的是代码随想录的大神文章觉得很棒。根节点是优先处理的节点那么入栈的时候就在入一个空节点,从而出栈的时候进行节点是否为空的判断来判断是否是要处理加入结果集的节点。

前序遍历

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }
}

中序遍历

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
    Stack<TreeNode> st = new Stack<>();
    if (root != null) st.push(root);
    while (!st.empty()) {
        TreeNode node = st.peek();
        if (node != null) {
            st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
            if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
            st.push(node);                          // 添加中节点
            st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。

            if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
        } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
            st.pop();           // 将空节点弹出
            node = st.peek();    // 重新取出栈中元素
            st.pop();
            result.add(node.val); // 加入到结果集
        }
    }
    return result;
}
}

后序遍历

class Solution {
   public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)         
                               
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
   }
}

二叉树的属性

1.对称二叉树

题目连接:https://leetcode.cn/problems/symmetric-tree/
思路:1.看两个子树是否对称
2.子问题就是判断两个节点是否为空或者是否相等
3.然后递归的判断A节点的左子树和B节点的右子树,A节点的右子树和B节点的左子树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return traverse(root.left,root.right);
    }
   boolean traverse(TreeNode n1,TreeNode n2){
       if(n1==null&&n2==null){
           return true;
       }
       if(n1==null||n2==null){
           return false;
       }
       if(n1.val!=n2.val){
           return false;
       }
       return traverse(n1.left,n2.right)&&traverse(n1.right,n2.left);
   }
}

2.二叉树的最大深度

题目连接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/
思路:递归求左右子树谁的高度更大,然后加一

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

3.二叉树的最小深度

题目连接:https://leetcode.cn/problems/minimum-depth-of-binary-tree/
递归思路:可以递归,但相对于最大深度,最小深度要考虑如果一边的子树是空的,则最小深度需要考虑另一半,而不是0加1

class Solution {
    /**
     * 递归法,相比求MaxDepth要复杂点
     * 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量
     */
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        if (root.left == null) {
            return rightDepth + 1;
        }
        if (root.right == null) {
            return leftDepth + 1;
        }
        // 左右结点都不为null
        return Math.min(leftDepth, rightDepth) + 1;
    }
}

非递归即层序遍历
思路:层序遍历的时候depth++的位置要好好想想,depth需要在一进入while循环的时候就加加操作,因为根节点也算1个高度。结束的时候就是最先左右子树为空遍历到根节点的时候。

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int depth = 0;
        while (!queue.isEmpty()){
            int size = queue.size();
            depth++;
            TreeNode cur = null;
            for (int i = 0; i < size; i++) {
                cur = queue.poll();
                //如果当前节点的左右孩子都为空,直接返回最小深度
                if (cur.left == null && cur.right == null){
                    return depth;
                }
                if (cur.left != null) queue.offer(cur.left);
                if (cur.right != null) queue.offer(cur.right);
            }
        }
        return depth;
    }
}

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

题目连接:https://leetcode.cn/problems/count-complete-tree-nodes/
问题思路:完全二叉树可能是满二叉树,而满二叉树求节点的公式很简单,对于满二叉树来说,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1,不是满二叉树按照正常求节点个数的递归求法。

这里插一嘴判断满二叉树的方法,一颗满二叉树,左右子树都是满的,所以同时向下递归的时候记录一个数,左右同时遍历结束,来看这个数就可以了
具体代码表现:

while(l!=null){
            l=l.left;
            lh++;
        }
        while(r!=null){
            r=r.right;
            rh++;
        }
        if(lh==rh){
            int len=(int)Math.pow(2,lh)-1;
            return len;
        }

完整代码

class Solution {
    public int countNodes(TreeNode root) {
        if(root==null){
            return 0;
        }
        TreeNode l=root,r=root;
        int lh=0,rh=0;
        while(l!=null){
            l=l.left;
            lh++;
        }
        while(r!=null){
            r=r.right;
            rh++;
        }
        if(lh==rh){
            int len=(int)Math.pow(2,lh)-1;
            return len;
        }
        return countNodes(root.left)+countNodes(root.right)+1;
    }
}

5.平衡二叉树

题目连接:https://leetcode.cn/problems/balanced-binary-tree/
思路:1.切入点就是左右子树高度差绝对值不超过1
2.所以得有个工具函数用来求高度
3.把两个子树高度求出来求差值
4.并且保证左右子树也是平衡的
完整代码

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root==null){
            return true;
        }
        TreeNode l=root.left;
        TreeNode r=root.right;
        int lh=0,rh=0;
       lh=depth(l);
       rh=depth(r);
       if(lh-rh<-1||lh-rh>1){
           return false;
       }
       return isBalanced(l)&&isBalanced(r);
    }
    int depth(TreeNode node){
        if(node==null){
            return 0;
        }
        int left=depth(node.left);
        int right=depth(node.right);
        return Math.max(left,right)+1;
    }
}

6.二叉树的所有路径

题目连接:https://leetcode.cn/problems/binary-tree-paths/
思路:1.一个二叉树每一条路径就是从根节点到叶子结点,这么多的结果在二叉树中需要用到回溯的知识
2.想要得到所有路径总体的核心还是遍历
3.要不断的得到新的路径得用到一个工具list方便添加和删除,很自然你想要方便的添加删除第一想法就是LinkedList

完整代码

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        traverse(root);
        return res;
    }
    LinkedList<String> path=new LinkedList<>();
    LinkedList<String> res=new LinkedList<>();
    void traverse(TreeNode root){
        if(root==null){
            return ;
        }
        if(root.left==null&&root.right==null){
            path.addLast(root.val+"");
            res.add(String.join("->",path));
            path.removeLast();
            return;
        }
        path.addLast(root.val+"");
        traverse(root.left);
        traverse(root.right);
        path.removeLast();
    }
}

7.左叶子之和

题目连接:https://leetcode.cn/problems/sum-of-left-leaves/
思路:1.主题还是递归,但是对于每个子问题是要好好考究的
2.判断是不是左叶子可以通过他的父节点判断

 if(root.left!=null&&root.left.left==null&&root.left.right==null){
            res+=root.left.val;
        }

完整代码

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        sumLeaf(root);
        return res;
    }
    int res=0;
    void sumLeaf(TreeNode root){
        if(root==null){
            return ;
        }
        if(root.left!=null&&root.left.left==null&&root.left.right==null){
            res+=root.left.val;
        }
        sumLeaf(root.left);
        sumLeaf(root.right);
    }
}

8.找树左下角的值

题目连接:https://leetcode.cn/problems/find-bottom-left-tree-value/
思路:1.你想求得是最底层最左边的节点的值,所以你需要找到最大的深度位置需要用到一个变量用来记录最大的深度,逐层遍历的时候更新这个最大的深度
2.找最左边就用前序遍历,往底层走的时候最先接触的一个节点就是左边的节点

完整代码

class Solution {
    int depth=0,maxDepth=0;
    TreeNode res=null;
    public int findBottomLeftValue(TreeNode root) {
        traverse(root);
        return res.val;
    }
    void traverse(TreeNode root){
        if(root==null){
            return;
        }
        depth++;
        if(depth>maxDepth){
            maxDepth=depth;
            res=root;
        }
        traverse(root.left);
        traverse(root.right);
        depth--;
    }
}

9.路径总和

题目连接:https://leetcode.cn/problems/path-sum/
思路:两种解决的方法都是有递归思想,一个是用一个变量记录路径求和过程的和比较直观,一个是减去当前节点的val值进行递归

完整代码

class Solution {
    
    int path=0;
    boolean found=false;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        traverse(root,targetSum);
        if(found){
            return true;
        }
        return false;
    }
    void traverse(TreeNode root,int targetSum){
        if(root==null){
            return;
        }
        path+=root.val;
        if(root.left==null&&root.right==null){
            if(path==targetSum){
                found=true;
            }
        }
        traverse(root.left,targetSum);
        traverse(root.right,targetSum);
        path-=root.val;
    }
}
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null){
            return false;
        }
        if(root.left==null&&root.right==null&&root.val==targetSum){
            return true;
        }
        return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val);
    }
}

二叉树的修改和改造

1.翻转二叉树

题目连接:https://leetcode.cn/problems/invert-binary-tree/

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null){
            return null;
        }
        TreeNode left=invertTree(root.left);
        TreeNode right=invertTree(root.right);
        root.left=right;
        root.right=left;
        return root;
    }
    
}

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

题目连接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
思路:转为list进行线性表的分割然后递归

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder==null||postorder==null){
            return null;
        }
        if(inorder.length==0||postorder.length==0){
            return null;
        }
        return mybuildTree(arrToList(inorder),arrToList(postorder));
    }
    List<Integer> arrToList(int[] arr){
        List<Integer> list=new ArrayList<>();
        for(int num:arr){
            list.add(num);
        }
        return list;
    }
    TreeNode mybuildTree(List<Integer> inorderList,List<Integer> postorderList){
        int len=postorderList.size();
        if(len==0){
            return null;
        }
        int rootVal=postorderList.get(postorderList.size()-1);
        int index=inorderList.indexOf(rootVal);
        TreeNode root=new TreeNode(rootVal);

        List<Integer> leftInorderList=inorderList.subList(0,index);
        List<Integer> rightInorderList=inorderList.subList(index+1,inorderList.size());

        List<Integer> leftPostorderList=postorderList.subList(0,index);
        List<Integer> rightPostOrderList=postorderList.subList(index,postorderList.size()-1);
        TreeNode left=mybuildTree(leftInorderList,leftPostorderList);
        TreeNode right=mybuildTree(rightInorderList,rightPostOrderList);
        root.left=left;
        root.right=right;
        return root;
    }
}

3.最大二叉树

题目连接:https://leetcode.cn/problems/maximum-binary-tree/
思路:1.首先对数组进行最大值的定位,找到最大值构造根节点,找到最大值的index进行左右子树的分割

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums==null||nums.length==0){
            return null;
        }
        return build(nums,0,nums.length-1);
    }
    TreeNode build(int[] nums,int left,int right){
        if(left>right){
            return null;
        }
        int max=Integer.MIN_VALUE;
        int index=0;
        for(int i=left;i<=right;i++){
            if(nums[i]>max){
                max=nums[i];
                index=i;
            }
        }
        TreeNode root=new TreeNode(max);
        TreeNode leftTree=build(nums,left,index-1);
        TreeNode rightTree=build(nums,index+1,right);
        root.left=leftTree;
        root.right=rightTree;
        return root;
    }
}

4.合并二叉树

题目连接:https://leetcode.cn/problems/merge-two-binary-trees/
思路:合并的时候,一个节点为空,合并后就是返回另一个节点。 节点都不为空的时候val相加,对于左右子树进行递归构造

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1==null){
            return root2;
        }
        if(root2==null){
            return root1;
        }
        root1.val+=root2.val;
        root1.left=mergeTrees(root1.left,root2.left);
        root1.right=mergeTrees(root1.right,root2.right);
        return root1;
    }
}

二叉搜索树的属性

1.二叉搜索树中的搜索

题目连接:https://leetcode.cn/problems/search-in-a-binary-search-tree/

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

2.验证二叉搜索树

题目连接:https://leetcode.cn/problems/validate-binary-search-tree/
思路:利用中序遍历升序的性质判断遍历过程中两个节点的大小

class Solution {
    TreeNode pre;
    public boolean isValidBST(TreeNode root) {
        if(root==null){
            return true;
        }
        boolean left=isValidBST(root.left);
        if(!left) return false;
        if(pre!=null&&root.val<=pre.val){
            return false;
        }
        pre=root;
        boolean right=isValidBST(root.right);
        if(!right) return false;
        return true;
    }
}

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

题目连接:https://leetcode.cn/problems/minimum-absolute-difference-in-bst/
思路:依旧和上一题有一点类似就是在中序遍历的过程中记录了pre节点,在遍历过程中不断地算差值进行更新。记得要不断更新pre值

class Solution {
    TreeNode pre;
    int res=Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        traverse(root);
        return res;
    }
   void traverse(TreeNode root){
       if(root==null){
           return ;
       }
       traverse(root.left);
       if(pre!=null){
           int tmp=root.val-pre.val;
           if(tmp<res){
               res=tmp;
           }
       }
       pre=root;
       traverse(root.right);

   }
}

4.二叉搜索树中的众数

题目连接:https://leetcode.cn/problems/find-mode-in-binary-search-tree/
思路:1.首先二叉搜索树的中序遍历是有序的,所以出现多次的数一定是连在一块的。
2.中序遍历过程中不断的记录相同的数出现了多少次,要有一个maxCount记录众数出现的次数
3.要有一个普通的count换了别的数也要记录,遍历过程中和maxCount比较,如果maxCount发生变化那么list就要进行清空

class Solution {
    List<Integer> resList=new ArrayList<>();
    TreeNode pre=null;
    int count=0;
    int maxCount=0;
    public int[] findMode(TreeNode root) {
        findMode01(root);
        int[] res=new int[resList.size()];
        for(int i=0;i<res.length;i++){
            res[i]=resList.get(i);
        }
        return res;
    }
    void findMode01(TreeNode root){
        if(root==null){
            return;
        }
        findMode01(root.left);
        if(pre==null||pre.val!=root.val){
            count=1;
        }else{
            count++;
        }
        if(count>maxCount){
            resList.clear();
            resList.add(root.val);
            maxCount=count;
        }else if(count==maxCount){
            resList.add(root.val);
        }
        pre=root;
        findMode01(root.right);
    }
}

5.二叉搜索树转成累加树

题目连接:https://leetcode.cn/problems/convert-bst-to-greater-tree/
思路:1.每个节点要加上比他大的所有值
2.可是中序遍历是从小遍历到最大的,遍历顺序反一下就是从大到小了

class Solution {
    public TreeNode convertBST(TreeNode root) {
        traverse(root);
        return root;
    }
    int sum=0;
    void traverse(TreeNode root){
        if(root==null){
            return;
        }
        traverse(root.right);
        sum+=root.val;
        root.val=sum;
        traverse(root.left);
    }
}

二叉树的公共祖先问题

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

题目连接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/
思路:情况 1,如果 p 和 q 都在以 root 为根的树中,那么 left 和 right 一定分别是 p 和 q(从 base case 看出来的)。

情况 2,如果 p 和 q 都不在以 root 为根的树中,直接返回 null。

情况 3,如果 p 和 q 只有一个存在于 root 为根的树中,函数返回该节点。
此外注意的是如果root等于其中一个节点的话,就返回root

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null){
            return null;
        }
        if(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){
            return null;
        }
        if(left==null){
            return right;
        }
        if(right==null){
            return left;
        }
        return root;
    }

}

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

题目连接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/
思路:与上一个不同的是这里用到了二叉搜索树的性质,对于参数总的两个节点,确定一个val值最小的节点,然后性质应用,根节点左边的小于根节点,根节点右边的大于根节点。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val>q.val){
            TreeNode tmp=p;
            p=q;
            q=tmp;
        }
        while(true){
            if(root.val>q.val){
                root=root.left;
            }else if(root.val<p.val){
                root=root.right;
            }else {
                return root;
            }
        }
        
    }
}

二叉搜索树的修改和改造

1.二叉搜索树的插入操作

题目连接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/
思路:1、寻找正确的插入位置,类似 700. 二叉搜索树中的搜索。

2、把元素插进去,这就要把新节点以返回值的方式接到父节点上。

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root==null){
            return new TreeNode(val);
        }
        if(root.val<val){
            root.right=insertIntoBST(root.right,val);
        }
        if(root.val>val){
            root.left=insertIntoBST(root.left,val);
        }
        return root;
    }
}

2.删除二叉搜索树的节点

题目连接:https://leetcode.cn/problems/delete-node-in-a-bst/
思路:情况 1:A 恰好是末端节点,两个子节点都为空,那么它可以当场去世了:

情况 2:A 只有一个非空子节点,那么它要让这个孩子接替自己的位置:

情况 3:A 有两个子节点,麻烦了,为了不破坏 BST 的性质,A 必须找到左子树中最大的那个节点或者右子树中最小的那个节点来接替自己,我的解法是用右子树中最小节点来替换:
情况1和情况2 体现在代码中就是对root.left和root.right的判空
情况3单独处理

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

3.修剪二叉树

题目连接:https://leetcode.cn/problems/trim-a-binary-search-tree/
思路:1、root.val < lo,这种情况下 root 节点本身和 root 的左子树全都是小于 lo 的,都需要被剪掉。
2、root.val > hi,这种情况下 root 节点本身和 root 的右子树全都是大于 hi 的,都需要被剪掉。

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root==null){
            return null;
        }
        if(root.val<low){
            return trimBST(root.right,low, high);
        }
        if(root.val>high){
            return trimBST(root.left, low, high);
        }
        root.left=trimBST(root.left,low,high);
        root.right=trimBST(root.right,low,high);
        return root;
    }
}

4.构造二叉搜索树

题目连接:https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/
思路:二叉树的构建问题很简单,说白了就是:构造根节点,然后构建左右子树。
一个有序数组对于 BST 来说就是中序遍历结果,根节点在数组中心,数组左侧是左子树元素,右侧是右子树元素。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return traverse(nums,0,nums.length-1);
    }
    TreeNode traverse(int[] nums,int left,int right){
        if(left>right){
            return null;
        }
        int mid=left+(right-left)/2;
        TreeNode root=new TreeNode(nums[mid]);
        root.left=traverse(nums,left,mid-1);
        root.right=traverse(nums,mid+1,right);
        return root;
    }
}

版权声明:本文为tyf4350158原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。