算法与数据结构之二叉树(Binary Tree)


一、什么是二叉树(Binary Tree)

定义:

二叉树是一种树形结构,其中每个节点最多有两个子节点,分别称为左子节点(left child)右子节点(right child)

每个节点最多有 2 个分支,这两个分支可以为空。


例子

复制代码
        A
       / \
      B   C
     / \   \
    D   E   F
  • A 是根节点(Root)
  • B、C 是 A 的孩子(Child)
  • D、E、F 是叶子节点(Leaf)
  • B 的父节点是 A
  • D 的兄弟节点是 E

二、二叉树的基本术语

名称 含义
根节点(Root) 没有父节点的节点
叶子节点(Leaf) 没有子节点的节点
父节点(Parent) 有子节点的节点
子节点(Child) 父节点的下属节点
兄弟节点(Sibling) 具有相同父节点的节点
节点深度(Depth) 从根到该节点的边数
节点高度(Height) 从该节点到叶子的最长路径
层级(Level) 节点的深度 + 1
满二叉树(Full Binary Tree) 所有非叶子节点都有两个子节点
完全二叉树(Complete Binary Tree) 除最后一层外,其他层节点全满,最后一层从左到右依次填充

三、二叉树的分类

类型 定义
普通二叉树 任意结构
满二叉树 所有非叶节点都有两个孩子,叶子都在同一层
完全二叉树 除最后一层外全满,最后一层从左向右填充
二叉搜索树(BST) 左子树 < 根 < 右子树
平衡二叉树(AVL Tree) 左右子树高度差 ≤ 1
红黑树(Red-Black Tree) 一种自平衡二叉搜索树
线索二叉树(Threaded Binary Tree) 用空指针指向前驱/后继节点以加速遍历

四、二叉树的性质

设二叉树中第 i i i 层最多有 2 i − 1 2^{i-1} 2i−1 个节点:

  1. 第 i i i 层最多节点数:
    N i = 2 i − 1 N_i = 2^{i-1} Ni=2i−1

  2. 深度为 k k k 的二叉树最多节点数:
    N max ⁡ = 2 k − 1 N_{\max} = 2^k - 1 Nmax=2k−1

  3. 具有 n n n 个节点的二叉树,高度至少为:
    h min ⁡ = ⌈ log ⁡ 2 ( n + 1 ) ⌉ h_{\min} = \lceil \log_2(n + 1) \rceil hmin=⌈log2(n+1)⌉

  4. 若叶子节点数为 n 0 n_0 n0,度为 2 的节点数为 n 2 n_2 n2,则:
    n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1


五、二叉树的存储方式

1.顺序存储(数组存储)

适用于 完全二叉树

设根节点下标为 1,则:

  • 左孩子:2*i
  • 右孩子:2*i + 1
  • 父节点:i / 2

例如:

复制代码
        A(1)
       /   \
   B(2)     C(3)
   / \     /
D(4) E(5) F(6)

数组存储:

复制代码
[ _, A, B, C, D, E, F ]

2.链式存储(指针结构)

cpp 复制代码
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

每个节点保存两个指针,分别指向左、右子节点。


六、二叉树的遍历(Traversal)

遍历是访问二叉树所有节点的过程。

1.深度优先遍历(DFS)

  • 前序遍历(Preorder) :根 → 左 → 右

    顺序:Root, Left, Right

  • 中序遍历(Inorder) :左 → 根 → 右

    顺序:Left, Root, Right

  • 后序遍历(Postorder) :左 → 右 → 根

    顺序:Left, Right, Root


示例

复制代码
        A
       / \
      B   C
     / \   \
    D   E   F
遍历类型 结果顺序
前序 A B D E C F
中序 D B E A C F
后序 D E B F C A

递归实现(以中序为例)

cpp 复制代码
void inorder(TreeNode* root) {
    if (!root) return;
    inorder(root->left);
    cout << root->val << " ";
    inorder(root->right);
}

2.广度优先遍历(BFS)

层序遍历(Level Order Traversal)

利用队列(Queue)实现。

cpp 复制代码
void levelOrder(TreeNode* root) {
    if (!root) return;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        TreeNode* node = q.front(); q.pop();
        cout << node->val << " ";
        if (node->left) q.push(node->left);
        if (node->right) q.push(node->right);
    }
}

结果:

复制代码
A B C D E F

七、二叉树的应用

应用场景 示例
表达式解析树 编译器、计算器((a+b)*c
二叉搜索树 BST 查找、排序
堆(Heap) 优先队列(最小堆、最大堆)
哈夫曼树(Huffman Tree) 数据压缩
决策树(Decision Tree) 机器学习
KD 树 / Octree 空间索引与点云处理

八、构建与销毁

cpp 复制代码
TreeNode* buildTree() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    return root;
}

void destroy(TreeNode* root) {
    if (!root) return;
    destroy(root->left);
    destroy(root->right);
    delete root;
}

九、复杂度与性能

操作 平均复杂度 最坏情况
插入 O ( log ⁡ n ) O(\log n) O(logn) O ( n ) O(n) O(n)(链化)
删除 O ( log ⁡ n ) O(\log n) O(logn) O ( n ) O(n) O(n)
查找 O ( log ⁡ n ) O(\log n) O(logn) O ( n ) O(n) O(n)
遍历 O ( n ) O(n) O(n) O ( n ) O(n) O(n)

为避免退化,可使用 AVL TreeRed-Black Tree


十、总结表

分类 特点 示例
普通二叉树 任意结构 一般树结构
满二叉树 每个非叶子都有两个孩子 理论分析
完全二叉树 从左至右填满
二叉搜索树 左 < 根 < 右 查找树
平衡树 高度差≤1 AVL、红黑树
线索树 空指针指向前驱/后继 遍历优化

十一、 综合示例-二叉搜索树(BST)

下面实现l一个完整的 Binary Search Tree 类,包括:

  • 节点定义
  • 插入节点
  • 查找节点
  • 删除节点
  • 三种深度优先遍历(前序 / 中序 / 后序)
  • 层序遍历
  • 销毁树

1.数据结构定义

cpp 复制代码
#include <iostream>
#include <queue>
using namespace std;

// 二叉树节点定义
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

2.二叉搜索树类定义

cpp 复制代码
class BinarySearchTree {
private:
    TreeNode* root;

    // 插入节点(递归)
    TreeNode* insert(TreeNode* node, int val) {
        if (!node) return new TreeNode(val);
        if (val < node->val) node->left = insert(node->left, val);
        else if (val > node->val) node->right = insert(node->right, val);
        return node;
    }

    // 查找节点(递归)
    bool search(TreeNode* node, int val) {
        if (!node) return false;
        if (val == node->val) return true;
        else if (val < node->val) return search(node->left, val);
        else return search(node->right, val);
    }

    // 找到最小值节点
    TreeNode* findMin(TreeNode* node) {
        while (node->left) node = node->left;
        return node;
    }

    // 删除节点
    TreeNode* remove(TreeNode* node, int val) {
        if (!node) return nullptr;
        if (val < node->val) node->left = remove(node->left, val);
        else if (val > node->val) node->right = remove(node->right, val);
        else {
            // 情况 1:无子节点
            if (!node->left && !node->right) {
                delete node;
                return nullptr;
            }
            // 情况 2:只有一个子节点
            else if (!node->left) {
                TreeNode* temp = node->right;
                delete node;
                return temp;
            } else if (!node->right) {
                TreeNode* temp = node->left;
                delete node;
                return temp;
            }
            // 情况 3:有两个子节点
            else {
                TreeNode* minNode = findMin(node->right);
                node->val = minNode->val;
                node->right = remove(node->right, minNode->val);
            }
        }
        return node;
    }

    // 遍历函数
    void preorder(TreeNode* node) {
        if (!node) return;
        cout << node->val << " ";
        preorder(node->left);
        preorder(node->right);
    }

    void inorder(TreeNode* node) {
        if (!node) return;
        inorder(node->left);
        cout << node->val << " ";
        inorder(node->right);
    }

    void postorder(TreeNode* node) {
        if (!node) return;
        postorder(node->left);
        postorder(node->right);
        cout << node->val << " ";
    }

    // 销毁整棵树
    void destroy(TreeNode* node) {
        if (!node) return;
        destroy(node->left);
        destroy(node->right);
        delete node;
    }

public:
    BinarySearchTree() : root(nullptr) {}
    ~BinarySearchTree() { destroy(root); }

    void insert(int val) { root = insert(root, val); }
    void remove(int val) { root = remove(root, val); }
    bool search(int val) { return search(root, val); }

    // 层序遍历
    void levelOrder() {
        if (!root) return;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            TreeNode* node = q.front(); q.pop();
            cout << node->val << " ";
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
    }

    // 打印不同遍历结果
    void printOrders() {
        cout << "Preorder: "; preorder(root); cout << "\n";
        cout << "Inorder: "; inorder(root); cout << "\n";
        cout << "Postorder: "; postorder(root); cout << "\n";
        cout << "Level order: "; levelOrder(); cout << "\n";
    }
};

3.主程序示例

cpp 复制代码
int main() {
    BinarySearchTree bst;

    // 插入节点
    bst.insert(50);
    bst.insert(30);
    bst.insert(70);
    bst.insert(20);
    bst.insert(40);
    bst.insert(60);
    bst.insert(80);

    cout << "Binary Search Tree traversals:\n";
    bst.printOrders();

    cout << "\nSearch for 60: " << (bst.search(60) ? "Found" : "Not Found") << "\n";

    cout << "\nRemove 70\n";
    bst.remove(70);

    cout << "After deletion:\n";
    bst.printOrders();
    return 0;
}

输出结果

复制代码
Binary Search Tree traversals:
Preorder: 50 30 20 40 70 60 80
Inorder: 20 30 40 50 60 70 80
Postorder: 20 40 30 60 80 70 50
Level order: 50 30 70 20 40 60 80

Search for 60: Found

Remove 70
After deletion:
Preorder: 50 30 20 40 80 60
Inorder: 20 30 40 50 60 80
Postorder: 20 40 30 60 80 50
Level order: 50 30 80 20 40 60

示例分析

操作 说明
插入 构建一棵 BST,左小右大
查找 二分性质快速定位
删除 三种情况(无子、单子、双子)
遍历 四种遍历全面展示树结构
销毁 递归释放所有节点,避免内存泄漏

相关推荐
gugucoding8 分钟前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL1 小时前
我的数据结构3——链表(link list)
数据结构·链表
a1117762 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜2 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry2 小时前
LeetCode 541. 反转字符串 II
算法
Jerry3 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手3 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议
Java小白笔记3 小时前
Codex config.toml配置实战指南
人工智能·算法·chatgpt·ai编程·集成学习
王老师青少年编程3 小时前
2026年6月GESP真题及题解(C++七级):消消乐
数据结构·c++·算法·真题·gesp·2026年6月
z小猫不吃鱼3 小时前
模型剪枝经典论文精读:Pruning Filters for Efficient ConvNets
算法·机器学习·剪枝