什么是二叉树?
二叉树是一种树形数据结构,每个节点最多有两个子节点,分别称为 左子节点 和 右子节点。二叉树的特点是:
-
每个节点最多有两个子节点。
-
左子节点和右子节点是有序的,不能随意交换。
举个例子:
1 / \ 2 3 / \ 4 5
这是一个二叉树,根节点是 1
,左子节点是 2
,右子节点是 3
,2
的左子节点是 4
,右子节点是 5
。
二叉树的存储方式
1. 普通存储(顺序存储)
普通存储是用数组来存储二叉树。数组的下标表示节点的位置,父节点和子节点之间的关系通过下标计算得出。
特点:
-
适合存储完全二叉树。
-
对于非完全二叉树,会浪费大量空间。
父子节点关系:
-
父节点下标为
i
,左子节点下标为2i + 1
,右子节点下标为2i + 2
。 -
子节点下标为
i
,父节点下标为(i - 1) / 2
。
2. 链式存储
链式存储是用链表来存储二叉树。每个节点包含数据、左子节点指针和右子节点指针。
特点:
-
适合存储任意二叉树。
-
内存利用率高,但需要额外的指针空间。
3. 线索二叉树
线索二叉树是对链式存储的优化,通过利用空指针指向节点的前驱或后继,方便遍历。
特点:
-
可以快速找到节点的前驱和后继。
-
适合频繁遍历的场景。
二叉树的遍历
二叉树的遍历是指按照某种顺序访问树中的所有节点。常见的遍历方式有:
-
先序遍历:根节点 -> 左子树 -> 右子树。
-
中序遍历:左子树 -> 根节点 -> 右子树。
-
后序遍历:左子树 -> 右子树 -> 根节点。
-
层次遍历:按层次从上到下、从左到右访问节点。
二叉树的增删查改
1. 增加节点
在二叉树中增加节点通常是指在指定位置插入一个新节点。
2. 删除节点
在二叉树中删除节点通常是指删除指定节点,并调整树的结构。
3. 查找节点
在二叉树中查找节点通常是指根据值查找节点。
4. 修改节点
在二叉树中修改节点通常是指修改指定节点的值。
C 语言实现二叉树
以下是二叉树的链式存储实现代码,包含初始化、遍历、增删查改等功能。
#include <stdio.h>
#include <stdlib.h>
// 定义二叉树节点结构体
typedef struct TreeNode {
int data; // 数据
struct TreeNode *left; // 左子节点指针
struct TreeNode *right; // 右子节点指针
} TreeNode;
// 创建新节点
TreeNode* CreateNode(int data) {
TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
// 先序遍历
void PreOrder(TreeNode *root) {
if (root == NULL) return;
printf("%d ", root->data); // 访问根节点
PreOrder(root->left); // 遍历左子树
PreOrder(root->right); // 遍历右子树
}
// 中序遍历
void InOrder(TreeNode *root) {
if (root == NULL) return;
InOrder(root->left); // 遍历左子树
printf("%d ", root->data); // 访问根节点
InOrder(root->right); // 遍历右子树
}
// 后序遍历
void PostOrder(TreeNode *root) {
if (root == NULL) return;
PostOrder(root->left); // 遍历左子树
PostOrder(root->right); // 遍历右子树
printf("%d ", root->data); // 访问根节点
}
// 层次遍历
void LevelOrder(TreeNode *root) {
if (root == NULL) return;
TreeNode *queue[100]; // 用队列实现层次遍历
int front = 0, rear = 0;
queue[rear++] = root; // 根节点入队
while (front < rear) {
TreeNode *node = queue[front++]; // 出队
printf("%d ", node->data); // 访问节点
if (node->left != NULL) {
queue[rear++] = node->left; // 左子节点入队
}
if (node->right != NULL) {
queue[rear++] = node->right; // 右子节点入队
}
}
}
// 插入节点
TreeNode* InsertNode(TreeNode *root, int data) {
if (root == NULL) {
return CreateNode(data); // 创建新节点
}
if (data < root->data) {
root->left = InsertNode(root->left, data); // 插入到左子树
} else {
root->right = InsertNode(root->right, data); // 插入到右子树
}
return root;
}
// 查找节点
TreeNode* FindNode(TreeNode *root, int data) {
if (root == NULL || root->data == data) {
return root; // 找到目标节点或遍历结束
}
if (data < root->data) {
return FindNode(root->left, data); // 在左子树中查找
} else {
return FindNode(root->right, data); // 在右子树中查找
}
}
// 删除节点
TreeNode* DeleteNode(TreeNode *root, int data) {
if (root == NULL) return root;
if (data < root->data) {
root->left = DeleteNode(root->left, data); // 在左子树中删除
} else if (data > root->data) {
root->right = DeleteNode(root->right, data); // 在右子树中删除
} else {
// 找到目标节点
if (root->left == NULL) {
TreeNode *temp = root->right;
free(root);
return temp; // 只有右子节点
} else if (root->right == NULL) {
TreeNode *temp = root->left;
free(root);
return temp; // 只有左子节点
}
// 有两个子节点,找到右子树的最小节点
TreeNode *temp = root->right;
while (temp->left != NULL) {
temp = temp->left;
}
root->data = temp->data; // 用最小节点的值替换当前节点
root->right = DeleteNode(root->right, temp->data); // 删除最小节点
}
return root;
}
int main() {
TreeNode *root = NULL;
// 插入节点
root = InsertNode(root, 5);
root = InsertNode(root, 3);
root = InsertNode(root, 7);
root = InsertNode(root, 2);
root = InsertNode(root, 4);
root = InsertNode(root, 6);
root = InsertNode(root, 8);
// 遍历二叉树
printf("先序遍历:");
PreOrder(root);
printf("\n");
printf("中序遍历:");
InOrder(root);
printf("\n");
printf("后序遍历:");
PostOrder(root);
printf("\n");
printf("层次遍历:");
LevelOrder(root);
printf("\n");
// 查找节点
TreeNode *target = FindNode(root, 4);
if (target != NULL) {
printf("找到节点:%d\n", target->data);
} else {
printf("未找到节点\n");
}
// 删除节点
root = DeleteNode(root, 5);
printf("删除节点后的中序遍历:");
InOrder(root);
printf("\n");
return 0;
}
二叉树的使用场景
-
搜索树:用于快速查找、插入和删除数据。
-
表达式树:用于表示数学表达式。
-
哈夫曼树:用于数据压缩。
总结
二叉树是一种非常重要的数据结构,适合表示层次关系和递归结构。通过学习二叉树的存储、遍历和操作,你可以更好地理解树形数据结构的原理和应用。希望通过这篇文章,你能轻松掌握二叉树的相关知识!