返回题库算法与数据结构刷题树与二叉搜索树 · 第 3 / 3 篇

算法编程题:树与二叉搜索树(8 题)

053 一次调用返回二叉树的前序、中序和后序遍历。

难度: 基础

方法签名: TreeNode? root

输入约束: 树结构无环

示例: 根 1,右子 2,2 的左子 3 → 前序[1,2,3]、中序[1,3,2]、后序[3,2,1]

目标复杂度: 时间 O(n),额外空间 O(h) 不计结果

查看参考答案

解题思路: 一次 DFS 在访问节点、左子树后和右子树后分别记录值。

不变量: 递归返回时当前子树的三种遍历结果都已完整追加。

public static class AlgCode053Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static int[][] Solve(TreeNode? root)
    {
        var preorder = new List<int>();
        var inorder = new List<int>();
        var postorder = new List<int>();
        Visit(root, preorder, inorder, postorder);
        return [preorder.ToArray(), inorder.ToArray(), postorder.ToArray()];
    }
    
    private static void Visit(
        TreeNode? node,
        List<int> preorder,
        List<int> inorder,
        List<int> postorder)
    {
        if (node is null) return;
        preorder.Add(node.Value);
        Visit(node.Left, preorder, inorder, postorder);
        inorder.Add(node.Value);
        Visit(node.Right, preorder, inorder, postorder);
        postorder.Add(node.Value);
    }
}

复杂度: 时间 O(n),递归栈 O(h),结果空间 O(n)。

边界用例: 空树;单节点;退化链形树。

面试追问:

  • 如何用显式栈实现迭代中序遍历?

054 计算二叉树按节点数定义的最大深度。

难度: 基础

方法签名: TreeNode? root

输入约束: 空树深度为 0

示例: [3,9,20,null,null,15,7] → 3

目标复杂度: 时间 O(n),额外空间 O(h)

查看参考答案

解题思路: 空节点返回零,非空节点返回左右最大深度加一。

不变量: 递归返回值等于当前子树从根到最深叶子的节点数。

public static class AlgCode054Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static int Solve(TreeNode? root)
    {
        if (root is null) return 0;
        return 1 + Math.Max(Solve(root.Left), Solve(root.Right));
    }
}

复杂度: 时间 O(n),递归栈 O(h)。

边界用例: 空树;单节点;退化链形树可能导致深递归。

面试追问:

  • 如何用 BFS 迭代计算深度并避免递归栈?

055 按层返回二叉树每一层的节点值。

难度: 进阶

方法签名: TreeNode? root

输入约束: 树结构无环

示例: [3,9,20,null,null,15,7] → [[3],[9,20],[15,7]]

目标复杂度: 时间 O(n),额外空间 O(w)

查看参考答案

解题思路: 队列按层出队,处理每层前记录当前队列长度。

不变量: 每轮开始时队列前 levelSize 个节点恰好属于当前层。

public static class AlgCode055Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static int[][] Solve(TreeNode? root)
    {
        if (root is null) return [];
        var result = new List<int[]>();
        var queue = new Queue<TreeNode>();
        queue.Enqueue(root);
        while (queue.Count > 0)
        {
            int count = queue.Count;
            var level = new int[count];
            for (int index = 0; index < count; index++)
            {
                TreeNode node = queue.Dequeue();
                level[index] = node.Value;
                if (node.Left is not null) queue.Enqueue(node.Left);
                if (node.Right is not null) queue.Enqueue(node.Right);
            }
            result.Add(level);
        }
        return result.ToArray();
    }
}

复杂度: 时间 O(n),队列峰值空间 O(w),结果空间 O(n)。

边界用例: 空树;单节点;极宽层。

面试追问:

  • 如何改为锯齿形层序遍历?

056 验证一棵树是否为严格二叉搜索树。

难度: 进阶

方法签名: TreeNode? root

输入约束: 严格 BST 不允许重复值

示例: [5,1,4,null,null,3,6] → false

目标复杂度: 时间 O(n),额外空间 O(h)

查看参考答案

解题思路: 递归传递当前节点允许的 long 下界和上界。

不变量: 进入节点时,祖先约束已经合并为当前有效开区间。

public static class AlgCode056Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static bool Solve(TreeNode? root)
        => Validate(root, long.MinValue, long.MaxValue);
    
    private static bool Validate(TreeNode? node, long lower, long upper)
    {
        if (node is null) return true;
        if (node.Value <= lower || node.Value >= upper) return false;
        return Validate(node.Left, lower, node.Value) &&
            Validate(node.Right, node.Value, upper);
    }
}

复杂度: 时间 O(n),递归栈 O(h)。

边界用例: 空树;int 边界;跨层违规;重复值。

面试追问:

  • 如果允许重复值统一放在右子树,边界条件如何修改?

057 原地翻转二叉树的左右子树。

难度: 进阶

方法签名: TreeNode? root

输入约束: 树结构无环,允许修改节点

示例: [4,2,7,1,3,6,9] → [4,7,2,9,6,3,1]

目标复杂度: 时间 O(n),额外空间 O(h)

查看参考答案

解题思路: 交换当前节点左右引用,再递归处理两个子树。

不变量: 递归返回时当前子树的每个节点都已交换左右孩子。

public static class AlgCode057Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static TreeNode? Solve(TreeNode? root)
    {
        if (root is null) return null;
        (root.Left, root.Right) = (Solve(root.Right), Solve(root.Left));
        return root;
    }
}

复杂度: 时间 O(n),递归栈 O(h)。

边界用例: 空树;单节点;退化链形树。

面试追问:

  • 如何用队列实现迭代翻转?

058 在线性时间内判断二叉树是否高度平衡。

难度: 进阶

方法签名: TreeNode? root

输入约束: 平衡定义为每个节点左右子树高度差不超过 1

示例: [3,9,20,null,null,15,7] → true

目标复杂度: 时间 O(n),额外空间 O(h)

查看参考答案

解题思路: 后序返回高度,发现不平衡时返回 -1 并向上传播。

不变量: 非负返回值是准确高度,-1 表示子树已经不平衡。

public static class AlgCode058Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static bool Solve(TreeNode? root) => Height(root) >= 0;
    
    private static int Height(TreeNode? node)
    {
        if (node is null) return 0;
        int left = Height(node.Left);
        if (left < 0) return -1;
        int right = Height(node.Right);
        if (right < 0 || Math.Abs(left - right) > 1) return -1;
        return Math.Max(left, right) + 1;
    }
}

复杂度: 时间 O(n),递归栈 O(h)。

边界用例: 空树;只在根平衡但子树不平衡;退化链形树。

面试追问:

  • 重复调用最大深度为什么会退化为 O(n²)?

059 返回普通二叉树中两个已存在节点的最近公共祖先。

难度: 进阶

方法签名: TreeNode root, TreeNode first, TreeNode second

输入约束: 两个目标节点均存在且按引用识别

示例: 根 3,目标 5 和 1 → 节点 3

目标复杂度: 时间 O(n),额外空间 O(h)

查看参考答案

解题思路: 当前节点命中即返回;左右子树都返回非空时当前节点是最近公共祖先。

不变量: 递归返回值表示当前子树中找到的目标或已经确定的祖先。

public static class AlgCode059Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public static TreeNode? Solve(
        TreeNode? root,
        TreeNode first,
        TreeNode second)
    {
        if (root is null ||
            ReferenceEquals(root, first) ||
            ReferenceEquals(root, second))
            return root;
        TreeNode? left = Solve(root.Left, first, second);
        TreeNode? right = Solve(root.Right, first, second);
        if (left is not null && right is not null) return root;
        return left ?? right;
    }
}

复杂度: 时间 O(n),递归栈 O(h)。

边界用例: 一个目标是另一个祖先;目标分居两侧;按引用而非值比较。

面试追问:

  • 如果树是 BST,如何利用有序性降低搜索范围?

060 实现可往返还原任意二叉树的序列化与反序列化。

难度: 综合

方法签名: TreeCodec Create()

输入约束: 节点值为 int,格式需要保留 null 结构

示例: [1,2,3,null,null,4,5] 序列化后可还原同构树

目标复杂度: 序列化与反序列化均为时间 O(n)、空间 O(n)

查看参考答案

解题思路: 使用前序遍历并为 null 写入 #,反序列化按同一顺序消费标记。

不变量: 每次读取一个标记后,递归准确消费该节点整棵子树的标记。

public static class AlgCode060Solution
{
    public sealed class TreeNode
    {
        public int Value;
        public TreeNode? Left;
        public TreeNode? Right;
        public TreeNode(int value) => Value = value;
    }
    
    public sealed class TreeCodec
    {
        public string Serialize(TreeNode? root)
        {
            var tokens = new List<string>();
            Write(root, tokens);
            return string.Join(',', tokens);
        }
    
        public TreeNode? Deserialize(string source)
        {
            string[] tokens = source.Split(',');
            int index = 0;
            return Read(tokens, ref index);
        }
    
        private static void Write(TreeNode? node, List<string> tokens)
        {
            if (node is null)
            {
                tokens.Add("#");
                return;
            }
            tokens.Add(node.Value.ToString());
            Write(node.Left, tokens);
            Write(node.Right, tokens);
        }
    
        private static TreeNode? Read(string[] tokens, ref int index)
        {
            string token = tokens[index++];
            if (token == "#") return null;
            var node = new TreeNode(int.Parse(token));
            node.Left = Read(tokens, ref index);
            node.Right = Read(tokens, ref index);
            return node;
        }
    }
    
    public static TreeCodec Solve() => new();
}

复杂度: 序列化与反序列化均为时间 O(n)、结果和递归空间 O(n)。

边界用例: 空树;负数;重复值;退化深树;非法格式需另加校验。

面试追问:

  • 只给前序值而不记录 null,为什么无法唯一还原普通二叉树?
当前分类

树与二叉搜索树

查看全部分类 →
  1. 01算法专题:树与二叉搜索树
  2. 02算法选择题:树与二叉搜索树(5 题)5 题
  3. 03算法编程题:树与二叉搜索树(8 题)8 题
ESC

输入关键词开始搜索