Leetcode 二叉树面试题详解

本文深入探讨二叉树的基本算法及高级应用,包括遍历、构建、序列化与反序列化、二叉搜索树验证及特殊二叉树问题。通过实例解析,提升算法理解和面试准备。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉树是面试中容易出现的题目,也是较难的题目,因为二叉树的算法常常涉及递归,而且比较有特点。

所以面对二叉树的技巧是,掌握基本的算法,以及常见的二叉树难题,对于其他问题,在基本算法上修改得到。

 

在面试中,最简单的题目就是二叉树的遍历问题。

一般包括二叉树的前序,中序,后序,层序遍历。其中前面三个的递归形式是很容易写出来的。

如果面试要求给出非递归实现(一般借助栈即可)

最容易实现是前序遍历,因为前序遍历与标准的DFS是非常像的,只需要使用栈,模仿层序BFS的操作即可实现,后续遍历可以在前序遍历的基础上修改,最后将答案反转一下得到。

比较难的是中序遍历,中序遍历需要用栈模拟中序遍历的过程,一直往左走,走到叶子,然后输出,再往右。

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        TreeNode* now = root;
        while(now || !st.empty()){
            while(now){
                st.push(now);
                now = now->left;
            }
            if(!st.empty()){
                now = st.top();
                st.pop();
                res.push_back(now->val);
                now = now->right;
            }
        }
        return res;
    }
};

二叉树的层序遍历,BFS,也是一定被下来的模板

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(root==nullptr){
            return res;
        }
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            vector<int> temp;
            int n = q.size();
            for(int i=0;i<n;i++){
                TreeNode* node = q.front();
                q.pop();
                temp.push_back(node->val);
                if(node->left){
                    q.push(node->left);
                }
                if(node->right){
                    q.push(node->right);
                }
            }
            res.push_back(temp);
        }
        return res;
    }
};

笔试(选择题)和面试中,也会涉及由前序遍历和中序遍历,构建二叉树或直接给出后续遍历的题目。

这道题目的难点,是数组起点和中点的判断,需要仔细。出错了不容易debug

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func buildTree(preorder []int, inorder []int) *TreeNode {
    preEnd, inEnd := len(preorder)-1, len(inorder)-1
    return helper(preorder,0,preEnd,inorder,0,inEnd)
}


func helper(preorder []int, preStart int, preEnd int,
            inorder []int, inStart int, inEnd int) *TreeNode{
    if preStart>preEnd{
        return nil
    }
    var mid int
    for i:=inStart;i<=inEnd;i++{
        if preorder[preStart] == inorder[i]{
            mid = i
            break
        }
    }
    root := &TreeNode{Val:preorder[preStart]}
    left := helper(preorder,preStart+1, preStart+mid-inStart, inorder, inStart, mid-1)
    right := helper(preorder,preStart+mid-inStart+1,preEnd, inorder, mid+1,inEnd)
    root.Left = left
    root.Right = right
    return root
}

更难一点的题目是序列化一棵二叉树,即把二叉树序列化为字符串,再将二叉树反序列回来。

这里简单的做法是用前序遍历加上空结点标记,递归的进行序列化

下面是序列化函数

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        return rserialize(root,"");
    }


    public String rserialize(TreeNode root, String str) {
        if(root==null) {
            str+="null,";
        }else{
            str += str.valueOf(root.val) + ",";
            str = rserialize(root.left, str);
            str = rserialize(root.right, str);
        }
        return str;
        
    }

反序列化的过程涉及到split函数,所以考虑使用Java语言实现,为了更便于递归,将数组转为链表,更容易写

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        String[] data_array = data.split(",");
        // 用链表是为了方便递归
        List<String> data_list = new LinkedList<String>(Arrays.asList(data_array));
        return rdeserialize(data_list);
    }


    public TreeNode rdeserialize(List<String> list){
        
        if (list.get(0).equals("null")) {
            list.remove(0);
            return null;
        }
        TreeNode root = new TreeNode(Integer.valueOf(list.get(0)));
        list.remove(0);
        root.left = rdeserialize(list);
        root.right = rdeserialize(list);
        return root;
    }

与二叉树升级来的题目就是二叉搜索树,这个题目通常在笔试(选择题)或者面试中考察。

经典的题目有判断一棵二叉树是否是二叉搜索树

注意,二叉搜索树,一定是左子数的所有节点都要小于根节点,右子数的所有节点都要大于根节点,所以不是简单的判断一下,递归就行。必须要记录每次的最大值以及最小值。

还有一种方法是使用中序遍历来做

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        return helper(root,LONG_MIN,LONG_MAX);
    }

    bool helper(TreeNode* root, long min, long max){
        if(root==nullptr) return true;
        if(root->val<=min || root->val>=max) return false;
        return helper(root->left,min,root->val) && helper(root->right,root->val,max);
    }
};

有特点的容易题目

101. Symmetric Tree

Easy

242151FavoriteShare

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

For example, this binary tree [1,2,2,3,4,4,3] is symmetric:

    1
   / \
  2   2
 / \ / \
3  4 4  3

 

But the following [1,2,2,null,3,null,3] is not:

    1
   / \
  2   2
   \   \
   3    3

 

Note:
Bonus points if you could solve it both recursively and iteratively

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSymmetric(root *TreeNode) bool {
    if root == nil{
        return true
    }
    return helper(root.Left,root.Right)
    
}

func helper(rootA, rootB *TreeNode) bool{
    if rootA == nil && rootB == nil{
        return true
    }else if rootA == nil || rootB == nil{
        return false
    }else if rootA.Val != rootB.Val{
        return false
    }else{
        return helper(rootA.Left,rootB.Right) && helper(rootA.Right, rootB.Left)
    }
        
    
}

543. Diameter of Binary Tree

Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

Example:
Given a binary tree 

          1
         / \
        2   3
       / \     
      4   5    

Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].

Note: The length of path between two nodes is represented by the number of edges between them.

这道题目可以转会为左子数的高度加上右子数高度的最大值。所以在求树高度的逻辑中,加入全局变量,res,不断判断左子树加右子树的最大值。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
        int res = 0;
        helper(root, res);
        return res;
    }
    int helper(TreeNode* root, int &res){
        if(root==nullptr) return 0;
        int left = helper(root->left,res);
        int right = helper(root->right,res);
        res = max(left+right,res);
        return max(left,right)+1;
    }
};

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值