课程 · #5 / 11

二叉树

一个节点,两个孩子——一切递归数据结构之源

Binary tree · traversal orders

pick a traversal → · preorder / inorder / postorder / level
init

动手试试:插入几个值。然后逐个试试各种遍历——前序、中序、后序、层序——看树下方的访问顺序依次亮起来。

为什么它重要

树是分层数据的天然结构:文件系统、HTML 文档、组织架构图、决策树。二叉树是二叉搜索树、堆以及表达式解析的根基。

核心思想

想象一棵家谱树,只不过每个人最多有两个孩子(左和右)。从单独一位根祖先开始。

这就是二叉树。 每个节点最多有两个孩子。

树解锁了组织数据的新方式:

不同的遍历以不同的顺序访问节点:

要点回顾

再深入一些

完全二叉树会先填满每一层再开始下一层(最后一层可以不填满)。满二叉树中每个节点要么有 0 个孩子,要么有 2 个孩子。完美二叉树则既完全又满。这些区分对堆的实现(要求完全)和树的平衡很重要。

"第 k 层最多 2ᵏ 个节点"这个公式可由归纳法导出:第 0 层只有一个节点();每一层最多是上一层的两倍。把从第 0 层到高度 h 的几何级数求和,得到树中最多 2^(h+1) − 1 个节点。反过来推:一棵有 n 个节点的平衡树,其高度为 ⌊log₂ n⌋

实战中的树

数学细节

树的性质
第 k 层最多节点数 2ᵏ
高度为 h 的树最多节点数 2ʰ⁺¹ − 1
n 个节点时的最小高度 ⌊log₂ n⌋
最大高度(退化情形) n − 1
遍历时间
O(n) —— 每个节点恰好被访问一次
空间
递归遍历 调用栈上 O(h)
迭代式(BFS) O(w),其中 w 是最宽那一层的宽度

实现

二叉树节点

struct TreeNode<T> {
    data: T,
    left: Option<Box<TreeNode<T>>>,
    right: Option<Box<TreeNode<T>>>,
}

// In-order traversal (recursive)
fn inorder(node: Option<&Box<TreeNode<T>>>) {
    if let Some(n) = node {
        inorder(n.left.as_ref());
        process(n.data);
        inorder(n.right.as_ref());
    }
}

遍历顺序

练习

三道题目,每道传授一种不同的递归形状:

浏览全部练习题