算法与数据结构之二叉树(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) | 用空指针指向前驱/后继节点以加速遍历 |
四、二叉树的性质
设二叉树中第 iii 层最多有 2i−12^{i-1}2i−1 个节点:
-
第 iii 层最多节点数:
Ni=2i−1N_i = 2^{i-1} Ni=2i−1 -
深度为 kkk 的二叉树最多节点数:
Nmax=2k−1N_{\max} = 2^k - 1 Nmax=2k−1 -
具有 nnn 个节点的二叉树,高度至少为:
hmin=⌈log2(n+1)⌉h_{\min} = \lceil \log_2(n + 1) \rceil hmin=⌈log2(n+1)⌉ -
若叶子节点数为 n0n_0n0,度为 2 的节点数为 n2n_2n2,则:
n0=n2+1n_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.链式存储(指针结构)
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 |
递归实现(以中序为例)
void inorder(TreeNode* root) {if (!root) return;inorder(root->left);cout << root->val << " ";inorder(root->right);
}
2.广度优先遍历(BFS)
即 层序遍历(Level Order Traversal)
利用队列(Queue)实现。
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 | 空间索引与点云处理 |
八、构建与销毁
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(logn)O(\log n)O(logn) | O(n)O(n)O(n)(链化) |
| 删除 | O(logn)O(\log n)O(logn) | O(n)O(n)O(n) |
| 查找 | O(logn)O(\log n)O(logn) | O(n)O(n)O(n) |
| 遍历 | O(n)O(n)O(n) | O(n)O(n)O(n) |
为避免退化,可使用 AVL Tree 或 Red-Black Tree。
十、总结表
| 分类 | 特点 | 示例 |
|---|---|---|
| 普通二叉树 | 任意结构 | 一般树结构 |
| 满二叉树 | 每个非叶子都有两个孩子 | 理论分析 |
| 完全二叉树 | 从左至右填满 | 堆 |
| 二叉搜索树 | 左 < 根 < 右 | 查找树 |
| 平衡树 | 高度差≤1 | AVL、红黑树 |
| 线索树 | 空指针指向前驱/后继 | 遍历优化 |
十一、 综合示例-二叉搜索树(BST)
下面实现l一个完整的 Binary Search Tree 类,包括:
- 节点定义
- 插入节点
- 查找节点
- 删除节点
- 三种深度优先遍历(前序 / 中序 / 后序)
- 层序遍历
- 销毁树
1.数据结构定义
#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.二叉搜索树类定义
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.主程序示例
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 80Search for 60: FoundRemove 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,左小右大 |
| 查找 | 二分性质快速定位 |
| 删除 | 三种情况(无子、单子、双子) |
| 遍历 | 四种遍历全面展示树结构 |
| 销毁 | 递归释放所有节点,避免内存泄漏 |
