面试算法之——二叉树

0. 总结

  1. 层序遍历,Queue,addLast、PollFirst
  2. 前序遍历:Stack,先进后出,先右后左,push,pop
  3. 中序遍历:Stack,先存左子树,再存右子树
  4. 后序遍历:Stack,addFirst将原栈顶往下压
  5. BST中序为递增,反中序为递减
  6. BST节点大小:左 < 根 < 右
  7. 前序和中序重建、中序和后序重建

1. 前序遍历:Stack。先进后出,push,pop

  1. 二叉树的前序遍历
class Solution {
    public List preorderTraversal(TreeNode root) {
        /*
        二叉树的前序遍历
        8.24
        */

        //BFS做法,栈存节点
        ArrayList result = new ArrayList<>();
        Stack stack = new Stack<>();

        if(root == null){
            return result;
        }else{
            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;
    }
}

class Solution {
    ArrayList result = new ArrayList<>();
    public List preorderTraversal(TreeNode root) {
        if(root != null){
            result.add(root.val);

            preorderTraversal(root.left);
            preorderTraversal(root.right);
        }

        return result;
    }
}

2. 层序遍历:Queue。先进先出。addLast、pollFirst

  1. 二叉树的序列化和反序列化
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        //8.29 

        StringBuilder result = new StringBuilder();
        result.append("[");

        LinkedList queue = new LinkedList<>();

        if(root == null){
            return "[]";
        }else{
            queue.addLast(root);
        }

        while(!queue.isEmpty()){
            TreeNode node = queue.pollFirst();

            if(node != null){
                result.append(node.val+",");
                queue.addLast(node.left);
                queue.addLast(node.right);
            }else{
                result.append("null,");
            }
        }

        result.deleteCharAt(result.length()-1);
        result.append("]");

        return result.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]")){
            return null;
        }

        //提取节点值
        String[] str = data.substring(1,data.length()-1).split(",");
        TreeNode root = new TreeNode(Integer.parseInt(str[0]));

        LinkedList queue = new LinkedList<>();
        queue.addLast(root);

        int index = 1;
        while(!queue.isEmpty()){
            TreeNode node = queue.pollFirst();
            //重建左右子树
            if(!str[index].equals("null")){
                node.left = new TreeNode(Integer.parseInt(str[index]));
                queue.addLast(node.left);
            }
            index++;

            if(!str[index].equals("null")){
                node.right = new TreeNode(Integer.parseInt(str[index]));
                queue.addLast(node.right);
            }
            index++;
        }

        return root;
    }
}
  1. 二叉树的Z形遍历
class Solution {
    public List> zigzagLevelOrder(TreeNode root) {
        /*
        二叉树Z字形打印
        8.24
        */

        ArrayList> result = new ArrayList<>();
        LinkedList queue = new LinkedList<>();

        if(root == null){
            return result;
        }else{
            queue.addLast(root);
        }

        while(!queue.isEmpty()){
            //层存储,因为要Z字形打印,因此使用LinkedList
            LinkedList list = new LinkedList<>();

            //要根据result的奇偶行来控制存储,先提取result.size()
            int floor = result.size();

            //提取队列长度
            int size = queue.size();
            for(int i=0;i
  1. 二叉树从上到下打印
class Solution {
    public List> levelOrder(TreeNode root) {
        /*
        二叉树的层序遍历:从上往下
        8.24
        */

        ArrayList> result = new ArrayList<>();
        LinkedList queue = new LinkedList<>();

        if(root == null){
            return result;
        }else{
            queue.addLast(root);
        }

        while(!queue.isEmpty()){
            //层存储
            ArrayList list = new ArrayList<>();

            //先提取队列长度
            int size = queue.size();
            for(int i=0;i
  1. 二叉树从下往上打印
class Solution {
    public List> levelOrderBottom(TreeNode root) {
        /*
        二叉树层序遍历:从下往上
        8.24
        */

        //BFS队列实现
        ArrayList> result = new ArrayList<>();
        LinkedList queue = new LinkedList<>();

        if(root == null){
            return result;
        }else{
            queue.addLast(root);
        }

        while(!queue.isEmpty()){
            //层存储,使用ArrayList
            ArrayList list = new ArrayList<>();
            
            int size = queue.size();
            for(int i=0;i

3. 中序遍历:Stack

  1. 二叉树中序遍历
class Solution {
    public List inorderTraversal(TreeNode root) {
        /*
        二叉树中序遍历
        8.24
        */

        //BFS栈实现
        ArrayList result = new ArrayList<>();
        Stack stack = new Stack<>();

        if(root == null){
            return result;
        }

        while(root != null || !stack.isEmpty()){

            //每次循环都先到达左子树最左
            while(root != null){
                stack.push(root);
                root = root.left;
            }

            //接收左子树最左节点,存入
            root = stack.pop();
            result.add(root.val);

            //将节点替换为右子树,重复上述操作
            root = root.right;
        }

        return result;
    }
}


class Solution {
    ArrayList result = new ArrayList<>();

    public List inorderTraversal(TreeNode root) {
        if(root != null){
            inorderTraversal(root.left);

            result.add(root.val);

            inorderTraversal(root.right);
        }
        return result;
    }
}

4. 后序遍历:Stack。先进后出。push,pop。addFirst

  1. 二叉树的后序遍历
class Solution {
    public List postorderTraversal(TreeNode root) {
        /*
        二叉树后序遍历:左右根
        8.24
        */

        //BFS栈实现
        LinkedList result = new LinkedList<>();
        Stack stack = new Stack<>();

        if(root == null){
            return result;
        }else{
            stack.push(root);
        }

        while(!stack.isEmpty()){
            //接收栈顶
            TreeNode node = stack.pop();

            //添加到头部,
            result.addFirst(node.val);

            //先进后出。因为是通过将节点值添加到头部实现左右根的顺序,因此,需要右子树先出栈
            if(node.left != null){
                stack.push(node.left);
            }

            if(node.right != null){
                stack.push(node.right);
            }
        }
        return result;
    }
}

class Solution {
    ArrayList result = new ArrayList<>();
    public List postorderTraversal(TreeNode root) {
        if(root != null){
            postorderTraversal(root.left);
            postorderTraversal(root.right);
            result.add(root.val);
        }
        return result;
    }
}

5. BST相关:中序遍历为递增,反中序遍历为递减

  1. BST中第k小的元素
class Solution {

    int result = 0;
    int count = 0;

    public int kthSmallest(TreeNode root, int k) {
        /*
        二叉搜索树中第k小的元素
        7.26
        */
        //BST中序为递增

        if(root == null){
            return result;
        }

        find(root,k);

        return result;
    }

    public void find(TreeNode root,int k){
        //递归出口
        if(root == null){
            return;
        }

        //左
        find(root.left,k);

        //层数计数+1
        count++;

        //如果count == k,提取当前节点值,返回
        if(count == k){
            result = root.val;
            return;
        }

        //右
        find(root.right,k);
    }
}
  1. BST转累加树
class Solution {

    //保存当前节点值
    int temp = 0;

    public TreeNode convertBST(TreeNode root) {
        /*
        二叉搜索树转累加树
        7.26
        */
        //BST反中序为递减序列,可以实现累加

        if(root == null){
            return null;
        }

        //右
        convertBST(root.right);

        //叠加上一次
        root.val += temp;

        //提取本次
        temp = root.val;

        //左
        convertBST(root.left);

        return root;
    }
}
  1. 有序数组转BST
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        /*
        有序数组转换为BST
        8.23
        */
        //BST中序遍历为有序序列,提取数组的中位数作为root节点,然后中序递归,建立BST

        if(nums.length == 0){
            return null;
        }

        //传入nums用于查找中位数,传入start和end用于计算中位数
        return recur(nums,0,nums.length-1);
    }

    public TreeNode recur(int[] nums,int start,int end){
        //递归出口
        if(start > end){
            return null;
        }

        //中位数
        int middle = start + (end - start)/2;

        //提取中位数作为root
        TreeNode root = new TreeNode(nums[middle]);

        //中序递归重建,左子树[start,middle-1],右子树[middle+1,end]
        root.left = recur(nums,start,middle-1);

        root.right = recur(nums,middle+1,end);

        return root;
    }
}
  1. 有序链表转BST
class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        /*
        有序链表转二叉搜索树
        7.25
        */
        //找中点,中序递归

        if(head == null){
            return null;
        }

        //头和尾
        return transfer(head,null);
    }

    public TreeNode transfer(ListNode head,ListNode tail){
        //递归出口
        if(head == tail){
            return null;
        }

        //快慢指针找中点
        ListNode fast = head;
        ListNode slow = head;
        while(fast != tail && fast.next != tail){
            fast = fast.next.next;
            slow = slow.next;
        }

        //提取中点slow作为root节点
        TreeNode root = new TreeNode(slow.val);

        root.left = transfer(head,slow);
        root.right = transfer(slow.next,tail);

        return root;
    }
}
  1. BST的最近公共祖先
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        /*
        二叉树最近公共祖先
        7.27
        */
        //递归左右子树

        //递归出口
        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){
            return right;
        }
        if(right == null){
            return left;
        }

        return root;
    }
}

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //8.29
        //遍历,BST,左根右,从小到大

        //while true循环,直到找到公共交点位置
        while(true){

            //左子树都比root节点小,右子树都比root节点大
            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{
                return root;
            }
        }
    }
}
  1. BST的后序遍历序列
class Solution {

    public boolean verifyPostorder(int[] postorder) {
        //8.29 

        //单调栈,倒序遍历,找到第一个比右子树小的值的,提取作为root节点,然后比较root节点和左子树的节点值,如果左子树存在比root大的值,返回false

        Stack stack = new Stack<>();
        
        int root = Integer.MAX_VALUE;

        for(int i=postorder.length-1;i>=0;i--){
            //判断当前节点值是否比root节点大,左子树判断
            if(postorder[i] > root){
                return false;
            }

            //while循环寻找root节点值w
            while(!stack.isEmpty() && postorder[i] < stack.peek()){
                root = stack.pop();
            }

            stack.push(postorder[i]);
        }

        return true;
    }
}

6. DFS相关

  1. 二叉树中最大路径和(随意节点出发)
class Solution {

    int result = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        //8.30

        //dfs,提取左右子树本次递归的最大深度

        if(root == null){
            return 0;
        }

        dfs(root);

        return result;
    }

    public int dfs(TreeNode root){
        if(root == null){
            return 0;
        }

        //左子树路径和
        int left = Math.max(dfs(root.left),0);

        //右子树路径和
        int right = Math.max(dfs(root.right),0);

        //本次最大路径和
        result = Math.max(root.val + left + right,result);

        //返回本次最大路径
        return root.val + Math.max(left,right);
    }
}
  1. 路径总和——随机节点
class Solution {

    /*
    路径总和,随机节点出发
    7.26
    */
    //DFS+回溯
    //这一题的难点在于: 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点) 
    //所以要计算
        //1. 以root节点dfs递归
        //2. 以root.left节点从root节点开始递归
        //3. 以root.right节点从root节点开始递归
        
    public int pathSum(TreeNode root, int sum) {

        //1. 递归出口
        if(root == null){
            return 0;
        }

        //2. 创建root节点递归结果
        int root_val = DFS(root,sum);

        //3. 创建root.left节点递归结果(要递归pathSum(),要重新从root.left的root节点开始递归)
        int left_val = pathSum(root.left,sum);

        //4. 创建root.right节点递归结果(要递归pathSum(),要重新从root.right的root节点开始递归)
        int right_val = pathSum(root.right,sum);

        //5. 返回结果
        return root_val + left_val + right_val;
    }

    public int DFS(TreeNode node,int sum){

        //1. 判断越界
        if(node == null){
            return 0;
        }

        //2. sum递减node.val
        sum -= node.val;

        //3. 如果sum为0,result为1,否则为0
        int result;
        
        if(sum == 0){
            result = 1;
        }else{
            result = 0;
        }

        //4. 继续递归左右子树(记得加上result,继续叠加)
        return result + DFS(node.left,sum) + DFS(node.right,sum);
    }
}
  1. 路径总和——根节点
class Solution {

    ArrayList> result = new ArrayList<>();
    ArrayList list = new ArrayList<>();

    public List> pathSum(TreeNode root, int sum) {
        /*
        路经总和,找路径组合
        8.23
        */

        dfs(root,sum);

        return result;
    }

    public void dfs(TreeNode node,int sum){
        //递归出口
        if(node == null){
            return;
        }

        //list先添加当前节点值
        list.add(node.val);
        //sum递减当前节点值
        sum -= node.val;

        //如果sum递减到0并且左右子树为空,result添加list。否则继续递归左右子树
        if(sum == 0 && node.left == null && node.right == null){
            result.add(new ArrayList<>(list));
        }else{
            dfs(node.left,sum);
            dfs(node.right,sum);
        }

        //回溯
        list.remove(list.size()-1);
    }
}
  1. 判断是否存在一条路径
class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        /*
        判断是否存在到某个值的路径总和
        8.23
        */
        //要求从根节点开始,所以sum先递减root.val

        if(root == null){
            return false;
        }

        sum -= root.val;

        //如果sum为0且左右子树都为空,说明存在一条路径
        if(sum == 0 && root.left == null && root.right == null){
            return true;
        }

        //递归左右子树,存在一条即可
        return hasPathSum(root.left,sum) || hasPathSum(root.right,sum);
    }
}

7. 递归相关

  1. 树的子结构
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        //8.30
        //B是否在A的左右子树中,或者B的节点值能和A的左右子树匹配

        if(A == null || B == null){
            return false;
        }

        return isSubStructure(A.left,B) || isSubStructure(A.right,B) || compare(A,B);
    }
    
    public boolean compare(TreeNode A,TreeNode B){
        //true出口,B能够匹配A
        if(B == null){
            return true;
        }
        //false出口,B不能匹配A
        if(A == null || B == null || A.val != B.val){
            return false;
        }

        return compare(A.left,B.left) && compare(A.right,B.right);
    }
}
  1. 打家劫舍树形
class Solution {

    public int rob(TreeNode root) {
        /*
        打家劫舍,树形
        7.26
        */
        //偷当前节点和不偷当前节点

        //判断空
        if(root == null){
            return 0;
        }

        //使用数组接收返回结果
        int[] result = MyRob(root);

        return Math.max(result[0],result[1]);
    }

    public int[] MyRob(TreeNode root){
        
        int[] result = new int[2];

        //递归出口  
        if(root == null){
            return result;
        }

        //左子树递归结果
        int[] left = MyRob(root.left);
        //右子树递归结果
        int[] right = MyRob(root.right);

        //偷当前节点:当前+不能偷左右
        result[0] = root.val + left[1] + right[1];
        //不偷当前:左右最大值
        result[1] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);

        return result;
    }
}
  1. 二叉树的右视图
class Solution {

    ArrayList result = new ArrayList<>();
    int depth = 0;

    public List rightSideView(TreeNode root) {
        /*
        二叉树的右视图,先递归右子树,搜完左子树就return。需要一个深度depth变量控制添加每一层的值
        8.23
        */

        if(root == null){
            return result;
        }

        //当前深度为0
        dfs(root,0);

        return result;
    }

    public void dfs(TreeNode root,int curDepth){
        //递归出口
        if(root == null){
            return;
        }

        //如果当前深度一致
        if(curDepth == depth){
            result.add(root.val);
            depth++;
        }

        //右视图,搜索右子树
        dfs(root.right,curDepth+1);
        dfs(root.left,curDepth+1);
    }
}
  1. 二叉树的最近公共祖先
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {

        /*
        二叉树的公共祖先
        7.26
        */
        //递归左右子树,如果左子树找不到,返回右子树递归结果,反之亦然

        //1. 判断越界
        if(root == null || p == root || q == root){
            return root;
        }

        //2. 递归左子树
        TreeNode left = lowestCommonAncestor(root.left,p,q);

        //3. 递归右子树
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        //4. 如果左子树为null,返回右子树结果
        if(left == null){
            return right;
        }

        //5. 如果右子树为null,返回左子树结果
        if(right == null){
            return left;
        }

        //6. 否则返回root
        return root;
    }
}
  1. 二叉树的最小深度
class Solution {
    public int minDepth(TreeNode root) {
        /*
        二叉树最小深度
        8.23
        */
        //要考虑左空右不空和右空左不空的情况

        if(root == null){
            return 0;
        }

        //如果左子树已经为空,返回右子树递归结果
        if(root.left == null && root.right != null){
            return minDepth(root.right) + 1;
        }

        //如果右子树为空,返回左子树递归结果
        if(root.right == null && root.left != null){
            return minDepth(root.left) + 1;
        }

        //提取最小值
        return Math.min(minDepth(root.left),minDepth(root.right)) + 1;
    }
}
  1. 二叉树的直径
class Solution {

    int max = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        /*
        二叉树直径
        7.26
        */
        //直径等于左子树深度+右子树深度

        if(root == null){
            return 0;
        }

        helper(root);

        return max;
    }

    public int helper(TreeNode root){
        if(root == null){
            return 0;
        }

        int left = helper(root.left);
        int right = helper(root.right);

        if(left + right > max){
            max = left+ right;
        }

        return Math.max(left,right)+1;
    }
}
  1. 对称二叉树
class Solution {
    public boolean isSymmetric(TreeNode root) {
        /*
        二叉树镜像,左右子树能否同时到达底部并且节点值一致
        8.23
        */

        if(root == null){
            return true;
        }

        return Mirror(root.left,root.right);
    }

    public boolean Mirror(TreeNode left,TreeNode right){
        
        //true出口
        if(left == null && right == null){
            return true;
        }

        //false出口
        if(left == null || right == null || left.val != right.val){
            return false;
        }

        return Mirror(left.left,right.right) && Mirror(left.right,right.left);
    }
}
  1. 平衡二叉树
class Solution {
    public boolean isBalanced(TreeNode root) {
        /*
        平衡二叉树:左右子树深度不超过1,且左右子树是平衡树
        8.23
        */
        
        //递归到树的底部,返回true
        if(root == null){
            return true;
        }

        return isBalanced(root.left) && isBalanced(root.right) && Math.abs(Depth(root.left) - Depth(root.right)) <= 1;
    }

    public int Depth(TreeNode root){
        if(root == null){
            return 0;
        }

        return Math.max(Depth(root.left),Depth(root.right)) + 1;
    }
}
  1. 翻转二叉树
class Solution {
    public TreeNode invertTree(TreeNode root) {
        /*
        翻转二叉树
        7.26
        */
        //递归左右子树
        
        //判断空
        if(root == null){
            return null;
        }

        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);

        root.left = right;
        root.right = left;

        return root;
    }
}
  1. 合并二叉树
class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        /*
        合并二叉树
        7.26
        */
        //递归,选取其中一棵树作为合并后的树

        if(t1 == null){
            return t2;
        }

        if(t2 == null){
            return t1;
        }

        t1.val += t2.val;

        t1.left = mergeTrees(t1.left,t2.left);
        t1.right = mergeTrees(t1.right,t2.right);

        return t1;
    }
}

8. 重建二叉树

  1. 前序和中序重建
class Solution {

    Map map = new HashMap<>();
    int[] pre;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        /*
        前序和中序构建二叉树
        7.25
        */
        //递归重建,map存中序,数组存前序

        pre = preorder;
        for(int i=0;i in_right){
            return null;
        }

        //提取前序的root节点
        TreeNode root = new TreeNode(        pre[pre_root]);

        //提取中序的root节点值
        int in_root = map.get(root.val);

        //根据前序的root节点重建左右子树
        root.left = recur(pre_root+1,in_left,in_root-1);

        root.right = recur(pre_root+in_root-in_left+1,in_root+1,in_right);

        return root;
    }
}
  1. 中序和后序重建
class Solution {

    Map map = new HashMap<>();
    int postindex;

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        /*
        中序和后序重建二叉树
        7.25
        */
        //递归重建,map存中序,index提取后序

        postindex = postorder.length-1;

        for(int i=0;i in_right){
            return null;
        }

        //提取后序的root接待你
        TreeNode root = new TreeNode(postorder[postindex--]);

        //提取中序的root节点值
        int in_root = map.get(root.val);

        //根据后序的root节点重建,先右后左
        root.right = recur(postorder,in_root+1,in_right);

        root.left = recur(postorder,in_left,in_root-1);

        return root;
    }
}
  1. 二叉树展开为链表
class Solution {
    public void flatten(TreeNode root) {
        /*
        二叉树展开为链表
        7.25
        */
        //递归左子树形成左链表,保存右子树,将右子树变成左子树,将左子树置空,while到右子树的最右端,拼接保存的右子树

        if(root == null){
            return;
        }

        //递归左右子树到底部
        flatten(root.left);
        flatten(root.right);

        //保存右子树
        TreeNode temp = root.right;

        //将右子树替换为左子树
        root.right = root.left;

        //将左子树置空
        root.left = null;

        //while到右子树的最右端
        while(root.right != null){
            root = root.right;
        }

        //将右子树拼接到底部
        root.right = temp;
    }
}

你可能感兴趣的:(面试算法之——二叉树)