我的面试准备过程--二叉树(更新中)

写在最前面

导师贪腐出逃美国,两年未归,可怜了我。拿了小米和美团的offer,要被延期,offer失效,工作重新找。把准备过程纪录下来,共勉。

二叉树的基础

结点定义

public class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;

    public TreeNode(int val){
        this.val = val;
    }
}

二叉树的遍历

前序遍历

  • 前序遍历,递归法

    public static void preorderTraversalRec(TreeNode root) {
        if(root == null){
            return;
        }
    
        System.out.print(root.val + " ");
        preorderTraversalRec(root.left);
        preorderTraversalRec(root.right);
    }
  • 前序遍历,迭代法
    思路:借助一个栈

    public static void preorderTraversal(TreeNode root) {
        if(null == root){
            return;
        }
    
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
    
        while(!stack.empty()){
            TreeNode cur = stack.pop();
    
            System.out.println(cur.val);
    
            //后入先出,因而先压右结点,再压左结点
            if(null != cur.right){
                stack.push(cur.right);
            }
    
            if(null != cur.left){
                stack.push(cur.left);
            }
    
        }
    }
    

中序遍历

  • 中序遍历,递归法

    public static void inorderTraversalRec(TreeNode root) {
        if(null == root){
            return;
        }
    
        inorderTraversalRec(root.left);
        System.out.print(root.val + " ");
        inorderTraversalRec(root.right);
    
    }
    
  • 中序遍历,迭代法

    public static void inorderTraversal(TreeNode root) {
        if(null == root){
            return;
        }
    
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
    
        while(true){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
    
            if(stack.empty()){
                break;
            }
    
            cur = stack.pop();
            System.out.print(cur.val + " ");
            cur = cur.right;
        }
    
    }

后序遍历

  • 后序遍历,递归法

    public static void postorderTraversalRec(TreeNode root) {
        if(null == root){
            return;
        }
    
        postorderTraversalRec(root.left);
        postorderTraversalRec(root.right);
        System.out.print(root.val + " ");
    }
  • 后序遍历,迭代法

    public static void postorderTraversal(TreeNode root){
        if(null == root){
            return;
        }
    
        Stack<TreeNode> s = new Stack<TreeNode>();
        Stack<TreeNode> output = new Stack<>();
    
        s.push(root);
        while(!s.empty()){
            TreeNode cur = s.pop();
            output.push(cur);
    
            if(cur.left != null){
                s.push(cur.left);
            }
    
            if(cur.right != null){
                s.push(cur.right);
            }
        }
    
        while(!output.empty()){
            System.out.print(output.pop().val + " ");
        }
    }
  • 分层遍历

    public static void levelTraversal(TreeNode root) {
        if(null == root){
            return;
        }
    
        Queue<TreeNode> queue = new LinkedList<>();
        queue.push(root);
    
        while(!queue.empty()){
            TreeNode cur = queue.removeFirst();
            System.out.print(cur.val + " ");
    
            if(cur.left != null){
                queue.add(cur.left);
            }
    
            if(cur.right != null){
                queue.add(cur.right);
            }
        }
    }

求二叉树结点的个数

  • 递归解法 时间复杂度O(n)

    public static int getNodeNumRec(TreeNode root){
        if(null != root){
            return 0;
        }
    
        return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
    }
  • 迭代解法 时间复杂度O(n)
    思路:与层级遍历相同,遍历的过程中纪录结点数

    public static int getNodeNum(TreeNode root){
        if(null != root){
            return 0;
        }
    
        int count = 1;
        Queue<TreeNode> queue = LinkedList<>();
        queue.add(root);
    
        while(!queue.empty()){
            TreeNode cur = queue.remove();
    
            if(cur.left != null){
                queue.add(cur.left);
                count++;
            }
    
            if(cur.right != null){
                queue.add(cur.right);
                count++;
            }
        }
    
        return count;
    }

求二叉树的深度(高度)

  • 递归解法 时间复杂度O(n)

    public static int getDepthRec(TreeNode root) {
        if(null != root){
            return 0;
        }
    
        int leftDepth = getDepthRec(root.left);
        int rightDepth = getDepthRec(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
    }
  • 迭代解法 时间复杂度O(n)

    public static int getDepth(TreeNode root){
        if(null != root){
            return 0;
        }
    
        int depth = 0;
        int curLevelNodes = 1;
        int nextLevelNodes = 0;
    
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
    
        while(!queue.empty()){
            TreeNode cur = queue.remove();
            curLevelNodes--;
    
            if(cur.left != null){
                nextLevelNodes++;
                queue.add(cur.left);
            }
    
            if(cur.right != null){
                nextLevelNodes++;
                queue.add(cur.right);
            }
    
            if(curLevelNodes == 0){
                depth++;
                curLevelNodes = nextLevelNodes;
                nextLevelNodes = 0;
            }
        }
    
        return depth;
    }

求二叉树第K层的节点个数

  • 递归解法
    思路:求以root为根的k层节点数目 等价于 求以root左孩子为根的k-1层(因为少了root那一层)节点数目 加上 以root右孩子为根的k-1层(因为少了root那一层)节点数目

    public static int getNodeNumKthLevelRec(TreeNode root, int k) {
        if(null != root || k < 0){
            return 0;
        }
    
        if(k == 1){
            return 1;
        }
    
        int leftNodeNumKth = getNodeNumKthLevelRec(root.left, k - 1);
        int rightNodeNumKth = getNodeNumKthLevelRec(root.right, k - 1);
        return leftNodeNumKth + rightNodeNumKth;
    }
  • 迭代法
    思路:与求解树深度的解法相同,需要

    public static int getNodeNumKthLevel(TreeNode root, int k){
        if(root == null || k < 0){
            return 0;
        }
    
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
    
        int curLevelNodes = 1;
        int nextLevelNodes = 0;
    
        while(!queue.empty && k > 0){
            TreeNode cur = queue.remove();
            curLevelNodes--;
    
            if(cur.left != null){
                queue.add(cur.left);
                nextLevelNodes++;
            }
    
            if(cur.right != null){
                queue.add(cur.right);
                nextLevelNodes++;
            }
    
            if(curLevelNodes == 0){
                curLevelNodes = nextLevelNodes;
                nextLevelNodes = 0;
                k--;
            }
        }
    
        return curLevelNodes;
    }

求二叉树中叶子节点的个数

  • 迭代法

    public static int getNodeNumLeaf(TreeNode root) {
        if(root == null){
            return 0;
        }
    
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
    
        int leafNodeNum = 0;
    
        while(!queue.empty()){
            TreeNode cur = queue.remove();
    
            if(cur.left != null){
                queue.add(cur.left);
            }
    
            if(cur.right != null){
                queue.add(cur.right);
            }
    
            if(cur.left == null && cur.right = null){
                leafNodeNum++;
            }
        }
        return leafNodeNum;
    }

两个二叉树之间的关系

判断两棵二叉树是否相同的树。

  • 递归法

    public static boolean isSameRec(TreeNode r1, TreeNode r2) {
        if(r1 == null && r2 == null){
            return true;
        }
    
        if(r1 == null || r2 == null){
            return false;
        }
    
        if(r1.val != r2.val){
            return false;
        }
    
        boolean leftRes = isSameRec(r1.left, r2.left);
        boolean rightRes = isSameRec(r1.right, r2.right);
    
        return leftRes && rightRes;
    }
  • 迭代法
    思路:遍历一遍,比对即可

    public static boolean isSame(TreeNode r1, TreeNode r2) {
        if(r1 == null && r2 == null){
            return true;
        }
    
        if(r1 == null || r2 == null){
            return false;
        }
    
        Stack<TreeNode> s1 = new Stack<>();
        Stack<TreeNode> s2 = new Stack<>();
    
        s1.push(r1);
        s2.push(r2);
    
        while(!s1.empty() && !s2.empty()){
            TreeNode n1 = s1.pop();
            TreeNode n2 = s2.pop();
    
            if(n1 == null && n2 == null){
                continue;
            }else if(n1 != null && n2 != null && n1.val == n2.val){
                s1.push(n1.right);
                s1.push(n1.left);
                s2.push(n2.right);
                s2.push(n2.left);
            }else{
                return false;
            }
        }
        return true;
     }

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

  • 递归解法

    思路:
    (1)如果二叉树为空,返回真
    (2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
    public static boolean isAVLRec(TreeNode root) {
        if(root == null){
            return true;
        }
    
        if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){
            return false;
        }
    
        return isAVLRec(root.left) && isAVLRec(root.right);
    }

树的镜像

判断两个树是否互相镜像

    public static boolean isMirrorRec(TreeNode r1, TreeNode r2){
   if(r1 == null && r2 == null){
       return true;
   }

   if(r1 == null || r2 == null){
       return false;
   }

   if(r1.val != r2.val){
       return false;
   }

   return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);
    }

求树的镜像

  • 递归解法
    (1)如果二叉树为空,返回空
    (2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树

    1. 破坏原来的树

      public static TreeNode mirrorRec(TreeNode root) {
      if(root == null){
         return null;
      }
      
      TreeNode left = mirrorRec(root.left);
      TreeNode right = mirrorRec(root.right);
      
      root.left = right;
      root.right = left;
      return root;
      }

    2.保存原来的树

    public static TreeNode mirrorCopyRec(TreeNode root) {
        if(root == null){
            return null;
        }
    
        TreeNode newRoot = new TreeNode(root.val);
        newRoot.left = mirrorCopyRec(root.right);
        newRoot.right = mirrorCopyRec(root.left);
    
        return newRoot;
    
    }
  • 迭代解法

    1. 破坏原来的树
    public static void mirror(TreeNode root) {
        if(root == null){
            return;
        }
    
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
    
        while(!stack.empty()){
            TreeNode cur = stack.pop();
    
            TreeNode tmp = cur.left;
            cur.left = cur.right;
            cur.right = tmp;
    
            if(cur.left != null){
                stack.push(cur.left);
            }
    
            if(cur.right != null){
                stack.push(cur.right);
            }
        }
    }
    1. 不能破坏原来的树,返回一个新的镜像树
    public static TreeNode mirrorCopy(TreeNode root){
        if(root == null){
            return null;
        }
    
        Stack<TreeNode> stack = new Stack<>();
        Stack<TreeNode> newStack = new Stack<>();
        stack.push(root);
        TreeNode newRoot = new TreeNode(root.val);
        newStack.push(newRoot);
    
        while(!stack.empty()){
            TreeNode cur = stack.pop();
            TreeNode newCur = newStack.pop();
    
            if(cur.left != null){
                stack.push(cur.left);
                newCur.right = new TreeNode(cur.left.val);
                newStack.push(newCur.right);
            }
    
            if(cur.right != null){
                stack.push(cur.right);
                newCur.left = new TreeNode(cur.right.val);
                newStack.push(newCur.left);
            }
        }
        return newRoot;
    }

求二叉树中两个节点的最低公共祖先节点

  • 递归法
    思路:1. 如果其中一个结点为根结点,则返回根结点

    1. 如果一个左子树找到,一个在右子树找到,则说明root是唯一可能的最低公共祖先
    2. 其他情况是要不然在左子树要不然在右子树
    public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {
        if (root == null || n1 == null || n2 == null) {
            return null;
        }
    
        if(root.equals(n1) || root.equals(n2)){
            return root;
        }
    
        TreeNode commonInLeft = getLastCommonParentRec(root.left, n1, n2);
        TreeNode commonInRight = getLastCommonParentRec(root.right, n1, n2);
        if(commonInLeft != null && commonInRight != null){
            return root;
        }
    
        if(commonInLeft == null){
            return commonInRight;
        }
    
        if(commonInRight == null){
            return commonInLeft;
        }
        return root;
    }
  • 迭代法

    public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2){
        if(root == null || n1 == null || n2 == null){
            return null;
        }
    
        List<TreeNode> list1= new ArrayList<>();
        List<TreeNode> list2 = new ArrayList<>();
    
        boolean res1 = getNodePath(root, n1, list1);
        boolean res2 = getNodePath(root, n2, list2);
    
        if(!res1 || !res2){
            return null;
        }
    
        Iterator<TreeNode> iter1 = list1.iterator();
        Iterator<TreeNode> iter2 = list2.iterator();
        TreeNode last = null;
    
        while(iter1.hasNext() && iter2.hasNext()){
            TreeNode tmp1 = iter1.next();
            TreeNode tmp2 = iter2.next();
    
            if(tmp1 == tmp2){
                last = tmp1;
            }else{
                break;
            }
        }
        return last;
    }
    
    private static boolean getNodePath(TreeNode root, TreeNode n, List<TreeNode> path){
        if(root == null){
            return false;
        }
    
        path.add(root);
        if(root == n){
            return true;
        }
    
        boolean found = false;
        found = getNodePath(root.left, n, path);
    
        if(!found){
            found = getNodePath(root.right, n, path);
        }
    
        if(!found){
            path.remove(root);
        }
    
        return found;
    }

本章参考http://blog.csdn.net/fightfor...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值