前言
二叉树(Binary Tree)相信大家不陌生吧,不管在面试还是平时开发中,都会经常用到,其定义为每个节点最多两个子节点的有序树。本文将对二叉树进行全面而深入的讲解,涵盖其基本概念、存储结构、遍历方法以及相关操作的实现细节,帮助大家更好地理解和应用这一数据结构。
一、二叉树的基本概念
首先先简单介绍一下二叉树的一些基本概念,主要与下面5点
- 节点:构成二叉树的基本单元,包含数据域和两个指向子节点的指针(通常称为左孩子指针和右孩子指针)。
- 根节点:二叉树的起始节点,没有父节点,位于树的最顶层。
- 叶子节点:没有子节点的节点,位于树的末端,通常用于表示数据的最终状态。
- 度:节点所拥有的子树数量。在二叉树中,节点的度数只能是0(叶子节点)、1(只有一个子节点)或2(有两个子节点)。
- 深度与高度:节点的深度是从根节点到该节点的路径长度;树的高度是从根节点到最远叶子节点的最长路径长度。
二、二叉树的存储结构
接下来是二叉树的存储结构主要有两种:链式存储和顺序存储。
链式存储
链式存储通过指针将各个节点连接起来,每个节点包含数据域和两个指针域(分别指向左孩子和右孩子)。这种存储方式灵活且易于插入和删除操作,但需要额外的内存空间来存储指针。
顺序存储
顺序存储利用数组下标来表示节点之间的关系。对于完全二叉树,可以通过数组下标快速计算出父子节点的位置。顺序存储节省内存空间,但插入和删除操作相对复杂。
本文主要探讨链式存储结构,后面用具体代码案例进行演示实现。
三、二叉树的遍历
另外一个重要的知识点就是,二叉树的遍历,遍历是指访问二叉树的所有节点的过程。常见的遍历方法有前序遍历、中序遍历和后序遍历。
- 前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树。
- 中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树。
- 后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。
四、二叉树的操作实现
上述介绍了二叉树的基本概念,接下来进入实操部分,以下是基于链式存储结构的二叉树操作实现,包括创建二叉树、遍历二叉树、求深度、统计节点个数和查找节点等。
1. 创建二叉树
首先通过先序序列创建二叉树的链式存储结构。先序序列中,空节点用#表示,具体代码如下:
void CreateBiTree(BiTree &T) {
TElemType ch;
cin >> ch;
if (ch == '#') {
T = NULL;
} else {
T = new BiTNode;
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
2. 遍历二叉树
前序遍历
用递归算法实现前序遍历二叉树,输出遍历序列,代码如下:
void PreOrderTraverse(BiTree T) {
if (T != NULL) {
cout << T->data << " ";
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
中序遍历
用递归算法实现中序遍历二叉树,输出遍历序列:
void InOrderTraverse(BiTree T) {
if (T != NULL) {
InOrderTraverse(T->lchild);
cout << T->data << " ";
InOrderTraverse(T->rchild);
}
}
后序遍历
用递归算法实现后序遍历二叉树,输出遍历序万
void PostOrderTraverse(BiTree T) {
if (T != NULL) {
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
cout << T->data << " ";
}
}
3. 非递归中序遍历
接下来使用栈实现非递归的中序遍历。
void InOrderTraverseNon(BiTree T) {
stack<BiTree> S;
BiTree p = T;
while (p != NULL || !S.empty()) {
while (p != NULL) {
S.push(p);
p = p->lchild;
}
if (!S.empty()) {
p = S.top();
S.pop();
cout << p->data << " ";
p = p->rchild;
}
}
}
4. 求二叉树深度
另一个重要的查询方法,查询二叉树深度,返回深度值
int Depth(BiTree T) {
if (T == NULL) return 0;
int leftDepth = Depth(T->lchild);
int rightDepth = Depth(T->rchild);
return max(leftDepth, rightDepth) + 1;
}
5. 统计二叉树节点个数
常见功能还有统计二叉树结点个数,返回二叉树结点个数
int Count(BiTree T) {
if (T == NULL) return 0;
return Count(T->lchild) + Count(T->rchild) + 1;
}
6. 查找节点
查找二叉树结点,在根指针为T的二叉树中查找值为x的结点,查找成功,p赋值为该结点指针,返回true;查找失败,p--NULL,返回false
bool Search(BiTree T, TElemType x, BiTree &p) {
if (T == NULL) return false;
if (T->data == x) {
p = T;
return true;
}
if (Search(T->lchild, x, p) || Search(T->rchild, x, p)) {
return true;
}
return false;
}
五、运行结果示例
通过上述代码,可以实现二叉树的创建、遍历、求深度、统计节点个数和查找节点等功能,增加main函数,并且通过命令窗口相关命令完成上述相关二叉树功能操作,完整代码如下:
int main(void) {
BiTree T = NULL;
int c = 0;
int d = 0;
int num = 0;
TElemType elem;
BiTree p = NULL;
while (c != 9) {
cout << endl << "1. 建立二叉树的二叉链表";
cout << endl << "2. 前序遍历二叉树";
cout << endl << "3. 中序遍历二叉树";
cout << endl << "4. 后序遍历二叉树";
cout << endl << "5. 非递归中序遍历二叉树";
cout << endl << "6. 求二叉树深度";
cout << endl << "7. 求二叉树结点个数";
cout << endl << "8. 查找结点";
cout << endl << "9. 退出";
cout << endl << "选择功能(1~9):";
cin >> c;
switch (c) {
case 1:
CreateBiTree(T);
break;
case 2:
PreOrderTraverse(T);
cout << endl;
break;
case 3:
InOrderTraverse(T);
cout << endl;
break;
case 4:
PostOrderTraverse(T);
cout << endl;
break;
case 5:
InOrderTraverseNon(T);
cout << endl;
break;
case 6:
d = Depth(T);
cout << "二叉树深度为:"<< d << endl;
break;
case 7:
num = Count(T);
cout << "二叉树结点个数为:" << num << endl;
break;
case 8:
cout << "请输入要查找的结点值:";
cin >> elem;
if (Search(T, elem, p))
cout << "结点查找成功!结点左孩子为:" << p->lchild << " 结点右孩子为:" << p->rchild << endl;
else
cout << "查找失败!" << endl;
break;
case 9:
cout << "结束操作" << endl;
break;
}
}
return 0;
}
运行结果,如图所示:
总结
本文全面且深入地剖析了二叉树,涵盖其基本概念,如节点、根节点、叶子节点和度的定义,存储结构,重点探讨了链式存储,遍历方法,包括前序、中序和后序遍历的详细步骤及代码实现;还介绍了操作实现,如创建、深度计算、节点统计和节点查找。此外,结合实际应用案例,展示了二叉树在数据库索引和编译原理等领域的重要作用,欢迎大家在评论区指导讨论。