数据结构之二叉树
数据结构之二叉树
- 数据结构之二叉树
- 1. 什么是二叉树?
- 2. 基本概念
- 3. 二叉树的基本形态
- 4. 二叉树的性质
- 5. 特殊二叉树
- 6. 二叉树的存储
- 7. 二叉树的遍历
- 7.1 前序遍历
- 7.2 中序遍历
- 7.3 后序遍历
- 7.4 层次遍历
- 8. 应用场景
- 9. 实例代码
- 9.1 示例树结构
- 9.2 输出结果
数据结构之二叉树
1. 什么是二叉树?
二叉树(Binary Tree)是一种特殊的树形数据结构,其中每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以为空(没有节点),或者由一个根节点和两棵互不相交的子树(左子树和右子树)组成。
二叉树的特点是:
- 每个节点最多有两个子节点。
- 左子树和右子树是有序的,不能互换。
2. 基本概念
- 节点(Node):二叉树的基本单位,包含数据域和指针域。
- 根节点(Root):二叉树的顶层节点,没有父节点。
- 叶子节点(Leaf):没有子节点的节点。
- 父节点(Parent):有子节点的节点。
- 子节点(Child):某个节点的直接下级节点。
- 深度(Depth):从根节点到某个节点的路径长度(根节点的深度为0)。
- 高度(Height):从某个节点到叶子节点的最长路径长度(叶子节点的高度为0)。
- 层次(Level):根节点为第0层,其子节点为第1层,以此类推。
3. 二叉树的基本形态
二叉树有以下几种基本形态:
- 空树:没有节点的二叉树。
- 只有一个根节点:根节点没有子节点。
- 只有左子树:根节点只有左子节点,右子节点为空。
- 只有右子树:根节点只有右子节点,左子节点为空。
- 左右子树都存在:根节点同时有左子节点和右子节点。
4. 二叉树的性质
二叉树具有以下重要性质:
- 性质1:在二叉树的第 i 层上,最多有 2^i 个节点 (i >= 0)。
- 例如:
- 第 0 层最多有 2^0 = 1 个节点(根节点)。
- 第 1 层最多有 2^1 = 2 个节点。
- 例如:
- 性质2:深度为 k 的二叉树,最多有 2^(k+1) - 1 个节点 (k >= 0)。
- 例如:
- 深度为 0 的二叉树,最多有 2^(0+1) - 1 = 1 个节点。
- 深度为 1 的二叉树,最多有 2^(1+1) - 1 = 3 个节点。
- 深度为 2 的二树,最多有 2^(2+1) - 1 = 7 个节点。
- 例如:
- 性质3:对于任意一棵二叉树,如果叶子节点数为 n0,度为 2 的节点数为 n2,则满足关系式:n0 = n2 + 1
- 说明:
- 叶子节点是指没有子节点的节点。
- 度为 2 的节点是指有两个子节点的节点。
- 说明:
- 性质4:具有 n 个节点的完全二叉树,其深度为:log2(n) 向下取整后加 1
- 说明:
- 完全二叉树是指除了最后一层外,其他层都是满的,最后一层的节点尽量靠左排列。
- 说明:
- 性质5:对于一棵完全二叉树,如果对节点从 0 开始编号,则:
- 父节点为 i,则:
- 左子节点编号为 2*i + 1
- 右子节点编号为 2*i + 2
- 子节点编号为 i,则:
- 父节点编号为 (i - 1) // 2 (整数除法)
- 父节点为 i,则:
5. 特殊二叉树
-
满二叉树(Full Binary Tree):
- 每一层的节点数都达到最大值,即第 i 层有 2^i 个节点。
- 深度为 k 的满二叉树,总节点数为 2^(k+1) - 1。
-
完全二叉树(Complete Binary Tree):
- 除了最后一层,其他层都是满的,且最后一层的节点尽量靠左排列。
- 适合用数组存储。
-
二叉搜索树(Binary Search Tree, BST):
- 左子树的所有节点值小于根节点值,右子树的所有节点值大于根节点值。
- 中序遍历结果是有序的。
-
平衡二叉树(Balanced Binary Tree):
- 任意节点的左右子树高度差不超过1。
- 例如:AVL树、红黑树。
6. 二叉树的存储
-
链式存储:
- 使用节点对象和指针表示二叉树。
- 每个节点包含数据域和两个指针域(左子节点和右子节点)。
struct TreeNode { int value; TreeNode* left; TreeNode* right; TreeNode(int val) : value(val), left(nullptr), right(nullptr) {} };
-
顺序存储:
- 使用数组存储完全二叉树。
- 对于节点 i,左子节点为 2i + 1,右子节点为 2i + 2,父节点为 (i - 1) // 2(其中
//
表示整数除法)
7. 二叉树的遍历
- 前序遍历(Pre-order):根 -> 左 -> 右
- 中序遍历(In-order):左 -> 根 -> 右
- 后序遍历(Post-order):左 -> 右 -> 根
- 层次遍历(Level-order):按层从上到下、从左到右访问节点。
7.1 前序遍历
void preOrderTraversal(TreeNode* root) {
if (root == nullptr) return; // 递归终止条件
cout << root->value << " "; // 访问根节点
preOrderTraversal(root->left); // 遍历左子树
preOrderTraversal(root->right); // 遍历右子树
}
7.2 中序遍历
void inOrderTraversal(TreeNode* root) {
if (root == nullptr) return; // 递归终止条件
inOrderTraversal(root->left); // 遍历左子树
cout << root->value << " "; // 访问根节点
inOrderTraversal(root->right); // 遍历右子树
}
7.3 后序遍历
void postOrderTraversal(TreeNode* root) {
if (root == nullptr) return; // 递归终止条件
postOrderTraversal(root->left); // 遍历左子树
postOrderTraversal(root->right); // 遍历右子树
cout << root->value << " "; // 访问根节点
}
7.4 层次遍历
#include <queue>
void levelOrderTraversal(TreeNode* root) {
if (root == nullptr) return;
queue<TreeNode*> q;
q.push(root); // 根节点入队
while (!q.empty()) {
TreeNode* node = q.front(); // 取出队首节点
q.pop();
cout << node->value << " "; // 访问当前节点
if (node->left != nullptr) {
q.push(node->left); // 左子节点入队
}
if (node->right != nullptr) {
q.push(node->right); // 右子节点入队
}
}
}
8. 应用场景
- 二叉搜索树:用于动态查找、插入和删除操作。
- 堆(Heap):用于优先队列。
- 哈夫曼树:用于数据压缩。
- 表达式树:用于编译器的语法分析。
9. 实例代码
- 节点定义:使用
TreeNode
结构表示二叉树的节点,每个节点包含一个值指向左右子节点的指针。 - 遍历方法:
- 前序、中序、后序遍历使用递归实现。
- 层次遍历使用队列实现,通过逐层访问节点。
- 树的构建:
createSampleTree
函数手动构建了一棵简单的完全二叉树。
#include <iostream>
#include <queue> // 用于层次遍历
using namespace std;
// 定义二叉树的节点结构
struct TreeNode {
int value; // 数据域
TreeNode* left; // 左子节点指针
TreeNode* right; // 右子节点指针
// 构造函数
TreeNode(int val) : value(val), left(nullptr), right(nullptr) {}
};
// 前序遍历:根 -> 左 -> 右
void preOrderTraversal(TreeNode* root) {
if (root == nullptr) return;
cout << root->value << " "; // 访问根节点
preOrderTraversal(root->left); // 遍历左子树
preOrderTraversal(root->right); // 遍历右子树
}
// 中序遍历:左 -> 根 -> 右
void inOrderTraversal(TreeNode* root) {
if (root == nullptr) return;
inOrderTraversal(root->left); // 遍历左子树
cout << root->value << " "; // 访问根节点
inOrderTraversal(root->right); // 遍历右子树
}
// 后序遍历:左 -> 右 -> 根
void postOrderTraversal(TreeNode* root) {
if (root == nullptr) return;
postOrderTraversal(root->left); // 遍历左子树
postOrderTraversal(root->right); // 遍历右子树
cout << root->value << " "; // 访问根节点
}
// 层次遍历:从到下,从左到右
void levelOrderTraversal(TreeNode* root) {
if (root == nullptr) return;
queue<TreeNode*> q; // 辅助队列
q.push(root); // 根节点入队
while (!q.empty()) {
TreeNode* node = q.front(); // 获取队首节点
q.pop(); // 出队
cout << node->value << " "; // 访问当前节点
if (node->left != nullptr) {
q.push(node->left); // 左子节点入队
}
if (node->right != nullptr) {
q.push(node->right); // 右子节点入队
}
}
}
// 创建一个简单的二叉树
TreeNode* createSampleTree() {
// 创建节点
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);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
return root; // 返回根节点
}
int main() {
// 创建示例二叉树
TreeNode* root = createSampleTree();
// 打印各类遍历结果
cout << "前序遍历 (Pre-order): ";
preOrderTraversal(root);
cout << endl;
cout << "中序遍历In-order): ";
inOrderTraversal(root);
cout << endl;
cout << "后序遍历 (Post-order): ";
postOrderTraversal(root);
cout << endl;
cout << "层次遍历 (Level-order): ";
levelOrderTraversal(root);
cout << endl;
return 0;
}
9.1 示例树结构
程序中创建的二叉树结构如下:
1
/ \
2 3
/ \ / \
4 5 6 7
9.2 输出结果
运行以上代码后,输出结果如下:
前序遍历 (Pre-order): 1 2 4 5 3 6 7
中序遍历 (In-order): 4 2 5 1 6 3 7
后序遍历 (Post-order): 4 5 6 7 3 1
层次遍历 (Level-order): 1 2 3 4 5 6 7