二叉树介绍
二叉树:
- 二叉树是由节点(或称为顶点)组成的数据结构,每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树的子树包括左子树和右子树,它们也都是二叉树。
常见的特殊类型的二叉树类型有满二叉树、完全二叉树、平衡二叉树(AVL树)、二叉搜索树(BST树)等等。
二叉树的遍历
- 二叉树遍历有几种常见的遍历方式:前序遍历、中序遍历、后序遍历、层序遍历。
前序遍历:先访问根节点,然后前序遍历左子树,最后前序遍历右子树。
中序遍历:先中序遍历左子树,然后访问根节点,最后中序遍历右子树。
后序遍历:先后序遍历左子树,然后后序遍历右子树,最后访问根节点。
层序遍历:按层级从上到下,从左到右访问每个节点。
二叉树的存储
链式存储:使用节点和指针,每个节点包含数据域和两个指针域。
数组存储:适用于完全二叉树,通过数组下标来表示节点之间的关系。
二叉树的应用
- 二叉搜索树用于快速查找、插入和删除数据。
- 堆是一种特殊的二叉树,用于实现优先队列。
- 二叉树还可以用于排序和平衡算法,如AVL树和红黑树。
二叉树常见操作
在树状领域中,最常见的操作是树的创建以及几种遍历方式,还有销毁树。
实现这些操作拥有多种方式,常见的有递归方式和非递归方式,均用到链式存储结构,下面进行介绍:
二叉树的递归操作
二叉链表结构定义
typedef char DataType;
typedef struct BTreeNode
{
DataType data;
struct BTreeNode* leftchild;//左子树
struct BTreeNode* rightchild;//右子树
}BinTreeNode;
typedef BTreeNode* BinTree;
递归创建二叉树
BinTree CreateBinTree_Recursion()
{
//code by FFNCL
char ch;
BinTree bt;
scanf("%c", &ch);//读取输入的字符
if (ch == '@')//输入@表示分支为空
bt = NULL;
else
{
bt = (BinTreeNode*)malloc(sizeof(BinTreeNode));
bt->data = ch;//数据域赋值
bt->leftchild = CreateBinTree_Recursion();//递归构造左子树
bt->rightchild = CreateBinTree_Recursion();//递归构造右子树
}
return bt;
}
递归先序遍历
void PreOrder_Recursion(BinTree bt)
{
//code by FFNCL
if (bt == NULL)
return;
printf("%c", bt->data);//先访问根节点
PreOrder_Recursion(bt->leftchild);//递归访问左子树
PreOrder_Recursion(bt->rightchild);//递归访问右子树
}
递归中序遍历
void InOrder_Recursion(BinTree bt)
{
if (bt == NULL)
return;
InOrder_Recursion(bt->leftchild);//先访问左子树
printf("%c", bt->data); //访问根节点
InOrder_Recursion(bt->rightchild);//递归访问右子树
}
递归后续遍历
void PostOrder_Recursion(BinTree bt)
{
//code by FFNCL
if (bt == NULL)
return;
PostOrder_Recursion(bt->leftchild);//先访问左子树
PostOrder_Recursion(bt->rightchild);
printf("%c", bt->data);//最后访问根节点
}
递归层次遍历
层次遍历需要按照层级从左到右访问,所以需要用到队列辅助去解决,关于队列的操作可以看文章后面给出的文章:
void LevelOrder(BinTree bt)
{
//code by FFNCL
BinTree p;
LinkQueue queue = SetNullQueue_link();
if (bt == NULL)
return;
p = bt;
EnQueue_link(queue, bt);
while (!IsNullQueue_Link(queue))
{
p = FrontQueue_link(queue);
DeQueue_link(queue);
printf("%c ", p->data);
if (p->leftchild != NULL)
EnQueue_link(queue, p->leftchild);
if (p->rightchild != NULL)
EnQueue_link(queue, p->rightchild);
}
printf("\n");
}
递归销毁二叉树
void DestroyBinTree(BinTree bt)
{
//code by FFNCL
if (bt != NULL)
{
DestroyBinTree(bt->leftchild);
DestroyBinTree(bt->rightchild);
free(bt);
}
}
二叉树的非递归操作
非递归的操作相对于递归操作来说比较麻烦,需要用到栈和队列去作为辅助实现
二叉树类型结构定义(与递归操作一样)
typedef char DataType;
typedef struct BTreeNode
{
DataType data;
struct BTreeNode* leftchild;
struct BTreeNode* rightchild;
}BinTreeNode;
typedef BinTreeNode* BinTree;
非递归创建二叉树
非递归创建树需要用队列去辅助实现,队列的操作可以参考文章末尾给出的文章,下面是链队列的应用案例:
BinTree CreateBinTree_NRecursion()
{
//code by FFNCL
LinkQueue queue = SetNullQueue_link();//创建空队列
BinTreeNode* s, * p, * bt=NULL;
printf("按层次输入二叉树节点,以#结束:\n");
char ch = getchar();//读取输入内容
int count = -1;//用于分辨根节点或
while (ch != '#')//以#代表空节点
{
s = NULL;//虚节点@
if (ch != '@')
{
s = (BinTreeNode*)malloc(sizeof(BinTreeNode));//申请新节点
s->data = ch;//数据域赋值
s->leftchild = NULL;//指针域赋值
s->rightchild = NULL;
}
EnQueue_link(queue, s);//入队
count++;
if (count == 0)//0为根节点
bt = s;
else
{
p = FrontQueue_link(queue);//取队头
if (s != NULL && p != NULL)
{
if (count % 2 == 1)//奇数作为左孩子插入
p->leftchild = s;
else
p->rightchild = s;//偶数作为右孩子插入
}
if (count % 2 == 0)
DeQueue_link(queue);//左右孩子处理完,队头结点出队
}
ch = getchar();//读取下一个结点
}
return bt;
}
非递归层次遍历
void LevelOrder(BinTree bt)
{
//code by FFNCL
BinTree p;
LinkQueue queue = SetNullQueue_link();
if (bt == NULL)
return;
p = bt;
EnQueue_link(queue, bt);
while (!IsNullQueue_Link(queue))
{
p = FrontQueue_link(queue);
DeQueue_link(queue);
printf("%c ", p->data);
if (p->leftchild != NULL)
EnQueue_link(queue, p->leftchild);
if (p->rightchild != NULL)
EnQueue_link(queue, p->rightchild);
}
printf("\n");
}
非递归先序遍历
void PreOrder_NRecursion(BinTree bt)
{
//code by FFNCL
LinkStack lstack;//链栈定义
lstack = SetNullStack_Link();//初始化
BinTreeNode* p;
Push_link(lstack, bt);//根节点入栈
while (!IsNullStack_link(lstack))
{
p = Pop_seq_return(lstack);//取栈头结点值
Pop_link(lstack);//出栈
printf("%c", p->data);//访问根结点
if (p->rightchild)//右子树不空则进栈
Push_link(lstack, p->rightchild);
if (p->leftchild)//左子树不空进栈
Push_link(lstack, p->leftchild);
}
}
非递归中序遍历
void InOrder_NRecursion(BinTree bt)
{
//code by FFNCL
LinkStack lstack;//链栈定义
lstack = SetNullStack_Link();//初始化
BinTree p = bt;
if (p == NULL)
return;
Push_link(lstack, bt);//根结点入栈
p = p->leftchild;//进入左子树
while (p || !IsNullStack_link(lstack))
{
while (p != NULL)
{
Push_link(lstack, p);
p = p->leftchild;
}
p = Pop_seq_return(lstack);//取栈头结点值
Pop_link(lstack);//出栈
printf("%c", p->data);//访问结点
p = p->rightchild;//右子树非空则扫描右子树
}
}
非递归后序遍历
void PostOrder_NRecursion(BinTree bt)
{
//code by FFNCL
LinkStack lstack;//定义链栈
lstack = SetNullStack_Link();//初始化
BinTree p = bt;
if (p == NULL)
return;
while (p || !IsNullStack_link(lstack))
{
while (p != NULL)
{
Push_link(lstack, p);//进栈
p = p->leftchild ? p->leftchild : p->rightchild;
}
p = Pop_seq_return(lstack);
Pop_link(lstack);
printf("%c", p->data);//访问结点
if (!IsNullStack_link(lstack) && (Pop_seq_return(lstack)->leftchild == p))
p = (Pop_seq_return(lstack))->rightchild;//从左子树退回,进入右子树
else
p = NULL;//从右子树退回,返回上一层
}
}
总结
二叉树是数据结构中树状类型的重要点,二叉树的基本操作尤为重要,是理解更复杂数据结构和算法的基础。
创作不易,感谢支持(q≧▽≦q)