常见面试算法题(树专题一)

124 阅读2分钟

引言:构造普通的二叉树

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;
    }
}

第一部分、验证二叉树的各种性质

1. 求二叉树的深度

leetcode-cn.com/problems/ma…

class Solution {
    //递归:在二叉树的左子节点和右子节点高度的较大值 + 1
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left) , maxDepth(root.right)) + 1;
    }
}

2. 求二叉树的最小深度

leetcode-cn.com/problems/mi…

class Solution {
    public int minDepth(TreeNode root) {
        if(root == null) return 0;
        int l = minDepth(root.left);
        int r = minDepth(root.right);
        if(l == 0) return r + 1;
        else if(r == 0) return l+1;
        return Math.min(l, r) + 1;
    }
}

3. 判断是不是平衡二叉树

leetcode-cn.com/problems/ba…

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        //满足下面三个条件
        return isBalanced(root.left) && isBalanced(root.right) && Math.abs(left-right) <= 1;
    }

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

4. 翻转二叉树

leetcode-cn.com/problems/in…

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        // 先翻转当前节点的左右子树
        TreeNode cur = root.left;
        root.left = root.right;
        root.right = cur;
        // 递归翻转左子节点和右子节点
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

5. 判断二叉树是不是对称的

leetcode-cn.com/problems/sy…


class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        return isMirror(root.left,  root.right);
    }

    boolean isMirror(TreeNode l , TreeNode r){
        if(l == null && r == null) return true;
        if(l == null || r == null) return false;
        if(l.val != r.val) return false;
        return isMirror(l.left , r.right) && isMirror(l.right , r.left);
    }
}

6. 判断二叉树是不是一样的

leetcode-cn.com/problems/sa…

public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null) return true;
        if(p == null || q == null) return false;
        if(p.val != q.val) return false;
        return isSameTree(p.left ,q.left) && isSameTree(p.right , q.right);
    }

7. 判断二叉树是不是另一颗二叉树的子树

leetcode-cn.com/problems/su…

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if(s == null) return false;
        if(t == null) return true;
        ////看是否是左/右子树的子树,或者与当前树相同
        return isSame(s , t) || isSubtree(s.left , t) || isSubtree(s.right , t);
    }

    boolean isSame(TreeNode s , TreeNode t){
        if(s == null && t == null) return true;
        if(s == null || t == null) return false;
        if(s.val != t.val) return false;
        return isSame(s.left , t.left) && isSame(s.right , t.right);
    }
}

8.二叉树的直径

leetcode-cn.com/problems/di…

class Solution {
    int res = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        if(root == null) return 0;
        dfs(root); //遍历每一个节点,以每一个节点为中心点计算最长路径(左子树边长+右子树边长)
        return res;
    }

    int dfs(TreeNode root){
        if(root.left == null && root.right == null) return 0;
        int leftSize = root.left == null ? 0 : dfs(root.left) + 1;
        int rightSize = root.right == null ? 0 : dfs(root.right) + 1;
        res = Math.max(res , leftSize + rightSize); //取的是路径数,更新res
        return Math.max(leftSize , rightSize);
    }
}

//解法2:以每个节点为中心获取左右子树的最大高度。
class Solution {
    int res = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        if(root == null) return 0;
        dfs(root);
        return res;
    }

    void dfs(TreeNode root){
        if(root == null)  return;
        int l = maxDepth(root.left);
        int r = maxDepth(root.right);
        res = Math.max(res , l + r);
        dfs(root.left);
        dfs(root.right);
    }

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

9. 二叉树的最大路径和

leetcode-cn.com/problems/bi…

class Solution {
    //和上一题一样,唯一的区别就是取左右子数的最大路径和的时候舍弃为负数的情况
    int res = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        if(root == null) return 0;
        dfs(root);
        return res;
    }

    int dfs(TreeNode root){
        if(root == null) return 0;
        int leftSum = Math.max(0 , dfs(root.left));
        int rightSum = Math.max(0 , dfs(root.right));
        res = Math.max(res , leftSum + rightSum + root.val);
        return Math.max(leftSum , rightSum) + root.val;
    }
}

10. 二叉树的最长同值路径

leetcode-cn.com/problems/lo…

//和前面两题思路一样
class Solution {
    int res = 0;
    public int longestUnivaluePath(TreeNode root) {
        if(root == null) return 0;
        dfs(root);
        return res;
    }

    int dfs(TreeNode root){
        if(root.left == null && root.right == null) return 0;
        int leftSize = root.left == null ? 0 : dfs(root.left)+1;
        int rightSize = root.right == null ? 0 : dfs(root.right)+1;
        
        //唯一的区别就是当前节点的值和左子树的节点值不同的时候,将相应的size = 0;
        if(root.left == null || root.val != root.left.val) leftSize = 0;
        if(root.right == null || root.val != root.right.val) rightSize = 0;
        res = Math.max(res , leftSize + rightSize);
        return Math.max(leftSize,rightSize);

    }
}

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

leetcode-cn.com/problems/lo…

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return root;
        if(root == p || root == q) return root;
        TreeNode l = lowestCommonAncestor(root.left , p , q);
        TreeNode r = lowestCommonAncestor(root.right , p , q);
        if(l == null) return r;
        if(r == null) return l;
        return root;
    }
}

第二部分、二叉树的遍历问题

1. 二叉树的先序遍历 --中左右

leetcode-cn.com/problems/bi…

//递归写法
class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root == null) return res;
        dfs(root , res);
        return res;
    }

    void dfs(TreeNode root , List<Integer> res){
        if(root == null) return;
        res.add(root.val);
        dfs(root.left , res);
        dfs(root.right, res);
    }
}

//非递归写法
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        // 用栈,先把root压入
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode curNode = stack.pop();
            res.add(curNode.val); //每弹出节点就加入res中
            //确保先压入右子节点再压入左子节点
            if(curNode.right != null) stack.push(curNode.right);
            if(curNode.left != null) stack.push(curNode.left);
        }
        return res;
    }
}

2. 二叉树的中序遍历--左中右

leetcode-cn.com/problems/bi…

//递归写法
class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null) return res;
        dfs(root , res);
        return res;
    }

    void dfs(TreeNode root , List<Integer> res){
        if(root == null) return;
        dfs(root.left , res);
        res.add(root.val);
        dfs(root.right, res);
    }
}

//非递归写法
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        // 左 中 右
        while(!stack.isEmpty() || cur != null){
            //1.先一路压左
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            //2. 弹出节点加入
            TreeNode curNode = stack.pop();
            res.add(curNode.val);
            //3. 最后加入右子节点
            cur = curNode.right;
        }
        return res;

    }
}

3. 二叉树的后序遍历--左右中

leetcode-cn.com/problems/bi…

//递归写法
class Solution {
     List<Integer> res = new ArrayList<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        if(root == null) return res;
        dfs(root , res);
        return res;
    }

    void dfs(TreeNode root , List<Integer> res){
        if(root == null) return;
        dfs(root.left , res);
        dfs(root.right, res);
        res.add(root.val);
    }
}
//非递归写法 --先序遍历倒过来
class Solution {
    //左右中
    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> res = new LinkedList<>();
        if(root == null) return res;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode curNode = stack.pop();
            res.addFirst(curNode.val);
            if(curNode.left != null) stack.push(curNode.left);
            if(curNode.right != null) stack.push(curNode.right);
        }
        return res;
    }
}

4. 二叉树的层序遍历

leetcode-cn.com/problems/bi…

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>(); //用队列记录每一层节点的个数
        queue.add(root);
        while(!queue.isEmpty()){
            int count = queue.size(); //这一层的节点数量
            List<Integer> list = new ArrayList<>();
            while(count > 0){
                TreeNode curNode = queue.poll();
                list.add(curNode.val);
                count--;
                if(curNode.left != null) queue.add(curNode.left); //将下一层的节点加入
                if(curNode.right != null) queue.add(curNode.right);
            }
            res.add(list);
        }
        return res;
    }
}

5. 二叉树的锯齿形遍历

leetcode-cn.com/problems/bi…

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>(); //用队列记录每一层节点的个数
        queue.add(root);
        while(!queue.isEmpty()){
            int count = queue.size(); //这一层的节点数量
            List<Integer> list = new ArrayList<>();
            while(count > 0){
                TreeNode curNode = queue.poll();
                list.add(curNode.val);
                count--;
                if(curNode.left != null) queue.add(curNode.left); //将下一层的节点加入
                if(curNode.right != null) queue.add(curNode.right);
            }
            if(res.size() % 2 == 1) Collections.reverse(list);
            res.add(list);
        }
        return res;
    }
}

6. 二叉树的右视图

leetcode-cn.com/problems/bi…

class Solution {
    //层序遍历,每次只保存最后一个值
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int count = queue.size();
            int tem = 0;
            while(count > 0){
                TreeNode curNode = queue.poll();
                tem = curNode.val;
                count--;
                if(curNode.left != null) queue.add(curNode.left);
                if(curNode.right != null) queue.add(curNode.right);
            }
            res.add(tem);
        }
        return res;
    }
}

7. 判断是不是完全二叉树

leetcode-cn.com/problems/ch…

class Solution {
    public boolean isCompleteTree(TreeNode root) {
        if(root == null) return true;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        //置一个flag,每一层遍历的时候如果遇到null就置为true,这一层如果又遇到null了就说明不是完全二叉树
        boolean flag = false;
        while(!queue.isEmpty()){
            TreeNode curNode = queue.poll();
            if(curNode  == null){
                flag = true;
                continue;
            }
            if(flag) return false;
            queue.add(curNode.left);
            queue.add(curNode.right);
        }
        return true;
    }
}

8.二叉树的最大宽度

leetcode-cn.com/problems/ma…

class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        if(root == null) return 0;
        int res = 1;
        LinkedList<TreeNode> queue = new LinkedList<>();
        LinkedList<Integer> indexQueue = new LinkedList<>();//记录节点的索引位置
        queue.add(root);
        indexQueue.add(1);
        while(!queue.isEmpty()){
            int count = queue.size();
            int l = indexQueue.peek();
            while(count > 0){
                TreeNode curNode = queue.poll();
                int r = indexQueue.poll(); // curNode的索引值
                count--;
                res = Math.max(res , r - l + 1); //更新 res
                if(curNode.left != null){
                    queue.add(curNode.left);
                    indexQueue.add(r * 2);
                }
                if(curNode.right != null){
                    queue.add(curNode.right);
                    indexQueue.add(r * 2 + 1);
                }
            }
        }
        return res;
    }
}

9. 二叉树每一行的最大值

leetcode-cn.com/problems/fi…

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

10. 二叉树的序列化和反序列化

leetcode-cn.com/problems/se…

public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null) return "[]";
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode curNode = queue.poll();
            if(curNode != null){
                sb.append(curNode.val).append(",");
                queue.add(curNode.left);
                queue.add(curNode.right);
            }else sb.append("null,");
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append("]");
        return sb.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]")) return null;
        String[] nums = data.substring(1 , data.length()-1).split(",");
        TreeNode root = new TreeNode(Integer.parseInt(nums[0]));
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int index = 1;
        while(!queue.isEmpty()){
            TreeNode curNode = queue.poll();
            if(!nums[index].equals("null")){
                curNode.left = new TreeNode(Integer.parseInt(nums[index]));
                queue.add(curNode.left);
            }
            index++;
            if(!nums[index].equals("null")){
                curNode.right = new TreeNode(Integer.parseInt(nums[index]));
                queue.add(curNode.right);
            }
            index++;
        }
        return root;
    }
}