【数据结构】二叉树面试题总结(持续更新中...)

目录

一、重要基础概念

二、二叉树的遍历

2.1 前、中、后序遍历(递归实现)

2.2 前、中、后序遍历(非递归实现)

2.3 二叉树的层序遍历

2.3.1 层序遍历代码

2.3.2 层序遍历变种OJ题目(左右视图)

2.4 二叉树的相关性质求解

2.4.1 求二叉树的大小

2.4.2 求叶子节点的数量

2.4.3 求第K层的节点数

三、二叉树基础练习题

3.1 相同的树

3.2 另一棵树的子树

3.3 二叉树的最大深度(高度)

3.4 平衡二叉树

3.5 对称二叉树

四、二叉树进阶练习题

4.1 根据字符串构建二叉树

4.2 二叉树的最近公共祖先

4.3 二叉树与双向链表

4.4 根据前序和中序遍历序列构建二叉树

4.5 根据前序和后序遍历序列构建二叉树

4.6 根据二叉树创建字符串


一、重要基础概念

树的深度:相对概念。每一层的深度不同。根节点的深度为1.

树的高度:最大的深度是高度。

满二叉树:每一层都放满。层数为K,结点总数为2的k次方-1 

完全二叉树:每一层从左到右依次存放。

二、二叉树的遍历

2.1 前、中、后序遍历(递归实现)

解题思路:

要点1:前、中、后序的遍历顺序相同,只是输出root的时机不同。

实现代码:

// 前序
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        result.add(root.val);
        List<Integer> leftTree = preorderTraversal(root.left);
        result.addAll(leftTree);
        List<Integer> rightTree = preorderTraversal(root.right);
        result.addAll(rightTree);
        return result;
    }
}
// 中序
class Solution {
    private List<Integer> result = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null){
            return result;
        }
        inorderTraversal(root.left);
        result.add(root.val);
        inorderTraversal(root.right);
        return result;
    }
}
// 后序
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        List<Integer> leftTree = postorderTraversal(root.left);
        result.addAll(leftTree);
        List<Integer> rightTree = postorderTraversal(root.right);
        result.addAll(rightTree);        
        result.add(root.val);
        return result;
    }
}

2.2 前、中、后序遍历(非递归实现)

OJ链接:

144. 二叉树的前序遍历 - 力扣(LeetCode)

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack();
        // 这个循环就是:按照中序遍历顺序,一直往下走,将所有遇到的不为null的节点记录下来
        while(root != null || !stack.empty()){
            while(root != null){
                stack.push(root);
                result.add(root.val);
                root = root.left;
            }
            // 走到这,root为空。以示例一为例,证明走到了3.left这个节点
            // 需要先将3这个节点从stack中拿出来,这样才能遍历他的右节点
            TreeNode tmp = stack.pop();
            root = tmp.right; // 现在到了3的右节点,假设他有,现在需要回到while
        }
        return result;
    }
}

 94. 二叉树的中序遍历 - 力扣(LeetCode)

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack();
        if(root == null) return result;
        while(root != null || !stack.empty()){
            while(root != null){
                stack.push(root);
                root = root.left;
            }
            TreeNode tmp = stack.pop();
            result.add(tmp.val);
            root = tmp.right;
        }
        return result;
    }
}

145. 二叉树的后序遍历 - 力扣(LeetCode)

class Solution {
    public int pos;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder == null || postorder == null){
            return null;
        }
        pos = postorder.length-1;
        return buildTree1(inorder,postorder,0,inorder.length-1);
        

    }
     public TreeNode buildTree1(int[] inorder, int[] postorder,int begin,int end){
         if(begin > end || begin < 0){
             return null;
         }
         int data = postorder[pos];
         
         TreeNode root = new TreeNode(data);
         int rootPos = findPos(inorder,data,begin,end);
         pos--;
         // 左右根  是从中序遍历的结果的最后一个节点往前找,因此,确定根节点后,应该先确定root.right
         root.right = buildTree1(inorder,postorder,rootPos+1,end);
         root.left = buildTree1(inorder,postorder,begin,rootPos-1);
         return root;

     }
     public int findPos(int[] inorder,int data,int begin,int end){
         for(int i=begin;i<=end;i++){
             if(inorder[i] == data){
                 return i;
             }
         }
         return -1;
     }
}

2.3 二叉树的层序遍历

2.3.1 层序遍历代码

OJ链接:102. 二叉树的层序遍历 - 力扣(LeetCode)

 解题思路:

要点1:定义一个队列实现层序遍历。队列中存放的是下一层的节点个数,队列的size就是下一层的节点数量。在遍历这一层的时候,将下一层的节点加入队列中

实现代码:

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> queque = new LinkedList<>();
        if(root == null){
            return result;
        }
        queque.offer(root);
        while(!queque.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            int size = queque.size();
            // 第一层 size = 1 ,将root的左右节点加入
            // 第二次 size = 2,while循环2次,将 root.left和root.right的左右节点加入
            while(size > 0){
                TreeNode top = queque.poll();
                tmp.add(top.val);
                if(top.left != null){
                    queque.offer(top.left);
                }
                if(top.right != null){
                    queque.offer(top.right);
                }
                size--;
            }
            result.add(tmp);
        }
        return result;
    }
}

2.3.2 层序遍历变种OJ题目(左右视图)

剑指 Offer II 046. 二叉树的右侧视图 - 力扣(LeetCode)

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<List<Integer>> olderLevelRsult = orderLevel(root);
        List<Integer> result = new ArrayList<>();
        if(root == null){
            return result;
        }

        for(List<Integer> tmp : olderLevelRsult){
            
            result.add(tmp.get(tmp.size()-1));
        }
        return result;
    }
    
    public List<List<Integer>> orderLevel(TreeNode root){
        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        if(root == null){
            return result;
        }
        queue.offer(root);
        while(!queue.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            int size = queue.size();
            while(size >0){
                TreeNode top = queue.poll();
                tmp.add(top.val);
                if(top.left != null){
                    queue.offer(top.left);
                }
                if(top.right != null){
                    queue.offer(top.right);
                }
                size--;
            }
            result.add(tmp);
        }
        return result;
    }
}

2.4 二叉树的相关性质求解

2.4.1 求二叉树的大小

 解题思路:

遍历求解:挨个遍历,采用前序遍历的思路,如果root!=null,size++

子问题思路:左树+右树+1

实现代码:

    // 子问题思路 
    // 获取总节点个数
    public int getSize(TreeNode root){
        if(root == null){
            return 0;
        }
        int left = getSize(root.left);
        int right = getSize(root.right);
        return 1+left+right;
    }
    // 遍历思路
    public int size = 0;
    private void getSize2(TreeNode root){
        if(root == null){
            return;
        }
        size++;
        getSize2(root.left);
        getSize2(root.right);
    }

2.4.2 求叶子节点的数量

解题思路:

要点1:叶子节点的判断条件:root.left==null&&root.right==null

实现代码:

    // 获取叶子节点个数
    public int num = 0;
    public void getLeafSize1(TreeNode root){
        if(root == null){
            return;
        }
        if(root.left==null && root.right == null){
            num++;
        }
        getLeafSize1(root.left);
        getLeafSize1(root.right);
    }
    public int getLeafSize2(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left==null && root.right == null){
            return 1;
        }
        int left = getLeafSize1(root.left);
        int right = getLeafSize2(root.right);
        return left+right;
    }

2.4.3 求第K层的节点数

 解题思路:

当k=1,证明到达第K层,返回1.不需要再向下遍历。

实现代码:

    public int getKLevelSize(TreeNode root,int k){
        if(root == null){
            return 0;
        }
        if(k==1){
            return 1;
        }
        int left = getKLevelSize(root.left,k-1);
        int right = getKLevelSize(root.right,k-1);
        return left+right;
    }
    public int KLevelSize = 0;
    public int getKLevelSize2(TreeNode root,int k){
        if(root == null){
            return 0;
        }
        if(k==1){
            KLevelSize++;
        }
        getKLevelSize(root.left,k-1);
        getKLevelSize(root.right,k-1);   
    }

三、二叉树基础练习题

3.1 相同的树

OJ链接:100. 相同的树 - 力扣(LeetCode)

问题描述:

解题思路:

要点1:先判断结构。再判断值。使用前序遍历的方式。

要点2:如果两个根节点都为null,认为这两个节点相同,返回true

要点3:如果根节点有一个不为null,结果不相同,返回false

要点4:如果都不为null。先判断值是否相同,不相同就返回false。相同,则接着判断这两个根的左节点和右节点

实现代码:

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null){
            return true;
        }
        if(p == null || q==null){
            return false;
        }
        // 走到这,都不为null,判断值
        if(p.val != q.val){
            return false;
        }else{
            return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right); 
        }
    }
}

3.2 另一棵树的子树

 OJ链接:572. 另一棵树的子树 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:采用判断两个树是否相同的思路。

要点2:如果subRoot是个空,返回true。空树是任何树的子树

要点3:如果root是空,返回false。空树没有子树

要点4:先判断root这整棵树和subRoot是否相等、不相等,接着判断

           root的左子树不为空的情况下,root的左子树这颗树和subRoot是否相等,不相等,接着判断

          root的右子树不为空的情况下, root的右子树这颗树和subRoot是否相等

实现代码:

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(subRoot == null) return true;
        if(root == null) return false;
        if(isSameTree(root,subRoot)){
            return true;
        }
        // 注意注意!!!判断的是:subRoot这棵树是否是root.left这棵树的子树
        if(root.left != null && isSubtree(root.left,subRoot)){
            return true;
        }
        if(root.right != null && isSubtree(root.right,subRoot)){
            return true;
        }
        return false;

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

3.3 二叉树的最大深度(高度)

 OJ链接:104. 二叉树的最大深度 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:root==null,返回0。

要点2:root不为空,返回1+max(左子树的高度,右子树的高度)

实现代码:

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

3.4 平衡二叉树

 OJ链接:110. 平衡二叉树 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:求每个节点的左树的高度和右树的高度,相减判断是否满足平衡二叉树的要求,时间复杂度是O(n2)

实现代码:

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null){
            return true;
        }
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        if(Math.abs(left - right) > 1){
            return false;
        }
        // root判断完后,需要接着判断左子树和右子树是否平衡
        return isBalanced(root.left)&&isBalanced(root.right);

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

  解题思路:

要点1:在刚刚的解法中,时间复杂度是O(n2),现在要使时间复杂度使O(n).

可以在递归的过程中,计算出左右子树的高度,判断高度是否<=1,如果不满足,返回-1.则证明不平衡

实现代码:

class Solution {
    public boolean isBalanced(TreeNode root) {
       
        return maxDepth(root) >= 0;

    }
    public int maxDepth(TreeNode root){
        if(root == null){
            return 0;
        }
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);

        if(left >= 0 && right >=0 && Math.abs(left - right) <= 1){
            return 1+Math.max(left,right);
        }
        return -1;
    }
}

3.5 对称二叉树

 OJ链接:101. 对称二叉树 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:如果root为空,则返回true。

要点2:写一个函数,输入是左树的根节点和右树的根节点

要点3:如果这连两个根节点不同时为null,返回false。同时为null,返回true。都不为null。接着判断两个根节点的值是否一样。一样接着判断左节点的左子树和右节点的右子树、左节点的右子树和右节点的左子树是否是对称的。

 实现代码:

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

    }
    public boolean isSymmetricChild(TreeNode leftNode,TreeNode rightNode){
        if(leftNode == null && rightNode==null){
            return true;
        }
        if(leftNode == null && rightNode != null || leftNode != null && rightNode == null ){
            return false;
        }
        if(leftNode.val != rightNode.val){
            return false;
        }
        return isSymmetricChild(leftNode.left,rightNode.right)&&isSymmetricChild(leftNode.right,rightNode.left);

    } 
}

四、二叉树进阶练习题

4.1 根据字符串构建二叉树

  OJ链接:二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

实现代码:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        while(scan.hasNext()){
            String str1 = scan.nextLine();
            TreeNode root = creatTree(str1);
            midorder(root);
            
        }
    }
    public static void midorder(TreeNode root){
        if(root == null){
            return;
        }
        midorder(root.left);
        System.out.print(root.val + " ");
        midorder(root.right);
    }
    public static int i;
    public static TreeNode creatTree(String str){
        
        TreeNode root = null;
        char ch1 = str.charAt(i);
        if(ch1 != '#'){
            root = new TreeNode(ch1);
            i++;
            root.left = creatTree(str);
            root.right = creatTree(str);
        }else{
            i++;
        }
        return root;
    }
}
class TreeNode{
    public char val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(char val){
        this.val = val;
    }
}

4.2 二叉树的最近公共祖先

 OJ链接:236. 二叉树的最近公共祖先 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:如果p和q有一个等于root,就返回root。

要点2:判断p和q是否在root的左子树或者右子树中。

实现代码:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        // 判断p与q是否等于root,如果等于,则证明root就是最近的公共节点
        if(p == root || q == root){
            return root;
        }
        // 判断p、q是否在root的左树里面存在,如果p或q等于左树的某个节点,就会返回这个节点的值
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        // 判断p、q是否在root的右树里面存在,如果p或q等于右树的某个节点,就会返回这个节点的值
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        // 如果即在左树里面存在,又在右树里面存在,就证明一个是在root的左子树中。另一个是在root的右子树中。他们的最近公共祖先是root
        if(left != null && right != null){
            return root;
        }
        // right为null,证明p、q在右子树都不存在,所以都在左子树中。所以left是最近公共祖先
        if(left != null && right == null){
            return left;
        }
        if(left == null && right != null){
            return right;
        }
        if(left == null && right == null){
            return null;
        }
        return null;
    }
}

4.3 二叉树与双向链表

  OJ链接:二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

实现代码:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        while(scan.hasNext()){
            String str1 = scan.nextLine();
            TreeNode root = creatTree(str1);
            midorder(root);
            
        }
    }
    public static void midorder(TreeNode root){
        if(root == null){
            return;
        }
        midorder(root.left);
        System.out.print(root.val + " ");
        midorder(root.right);
    }
    public static int i;
    public static TreeNode creatTree(String str){
        
        TreeNode root = null;
        char ch1 = str.charAt(i);
        if(ch1 != '#'){
            root = new TreeNode(ch1);
            i++;
            root.left = creatTree(str);
            root.right = creatTree(str);
        }else{
            i++;
        }
        return root;
    }
}
class TreeNode{
    public char val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(char val){
        this.val = val;
    }
}

4.4 根据前序和中序遍历序列构建二叉树

  OJ链接:

问题描述:

 解题思路:

要点1:root==null,返回0。

要点2:root不为空,返回1+max(左子树的高度,右子树的高度)

实现代码:

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

4.5 根据前序和后序遍历序列构建二叉树

 OJ链接:105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:root==null,返回0。

要点2:root不为空,返回1+max(左子树的高度,右子树的高度)

实现代码:

class Solution {
    public int pos = 0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || inorder == null){
            return null;
        }
        return buildTree1(preorder,inorder,0,inorder.length-1);
    }
    public TreeNode buildTree1(int[] preorder,int[] inorder,int begin,int end){
        if(begin > end){
            return null;
        }
        // 拿到前序遍历结果的第一个值
        int data = preorder[pos];
        // 第一值就是root,构建一个节点
        TreeNode root = new TreeNode(data);
        int rootPos = findIndex(inorder,data,begin,end);
        pos++;
        root.left =buildTree1(preorder,inorder,begin,rootPos-1);
        root.right = buildTree1(preorder,inorder,rootPos+1,end);
        return root;
    }
    public int findIndex(int[] inorder,int data,int begin,int end){
        // 查找data在中序遍历结果中的位置
        for(int i= begin;i<=end;i++){
            if(inorder[i] == data){
                return i;
            }
        }
        return -1;
    }
}

4.6 根据二叉树创建字符串

  OJ链接:606. 根据二叉树创建字符串 - 力扣(LeetCode)

问题描述:

 解题思路:

要点1:root==null,返回0。

要点2:root不为空,返回1+max(左子树的高度,右子树的高度)

实现代码:

class Solution {
    public StringBuffer sb = new StringBuffer();

    public void tree2strChild(TreeNode root) {
        if(root == null){
            return;
        }
        sb.append(root.val);
        if(root.left != null){
            sb.append("(");
            tree2strChild(root.left);
            sb.append(")");
        }else{
            if(root.right == null){
                return;
            }else{
                sb.append("()");
            }
        }
        if(root.right == null){
            return;
        }else{
            sb.append("(");
            tree2strChild(root.right);
            sb.append(")");
        }

    }

    public String tree2str(TreeNode root) {
        if(root == null){
            return null;
        }
        tree2strChild(root);
        return sb.toString();
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘减减

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值