前言
本文作为我学习数据结构的笔记,若有不足请见谅或麻烦提出宝贵意见。
1.线索二叉树的目的与意义
当二叉树用左右指针表示时,对于n个结点的二叉树,将存在n+1个空指针的空间浪费。
同时周游是二叉树中许多操作的基础,因此生成一种利用空指针的思想,利用结点的空左指针存储该结点在某种周游序列中的前驱结点的位置;利用结点的空右指针存储该结点在同种周游序列中的后继结点的位置。
这种附加的指向前驱结点和后继结点的指针称作线索,加进了线索的二叉树左右指针表示称作线索二叉树。
把二叉树左右指针表示改造成线索二叉树的过程称为线索化。
本文主要描述中序线索化。
2.结点结构
由于引入了线索的概念,需要添加一个标志位来表示指针是指向孩子还是线索。
其中左(右)标志为1,即指向前驱(后继);左(右)标志为0,即指向左(右)孩子。
3.算法实现
3.1 数据结构
首先是线索二叉树的数据结构:
// 线索二叉树存储结构
struct ThrTreeNode; // 结点类型
typedef struct ThrTreeNode * PThrTreeNode; // 结点指针类型
struct ThrTreeNode
{
char info; // 结点信息
PThrTreeNode llink; // 左孩子
PThrTreeNode rlink; // 右孩子
int ltag,rtag; // 标志,0为孩子,1为线索
} ;
typedef struct ThrTreeNode * ThrTree; // 线索二叉树类型
typedef ThrTree * PThrTree; // 线索二叉树指针类型
3.2 中序线索化
那么对于一个普通的二叉树,如何将其线索化成线索二叉树呢。
首先这里需要创建一个包含标志位的二叉树,初始状态所有结点的标志均为0。
注:这里的创建二叉树可以参考我的另一篇文章c语言数据结构——二叉树的先序、中序、后序遍历(借助栈) 在其中创建部分添加对标志位的初始化即可。同时线索化会借助栈,而栈的定义也和此文章大同小异。
1.算法思想
1)p指向树根,pr=NULL;(这里pr指刚访问过的结点,即p的前驱)
2)当p不为空,p进栈,置p为p的左孩子;重复直到p为空;(这里是走到二叉树的首结点)
3)当栈不空,p=栈顶,访问栈顶:
a.若p没有左孩子,置p的左指针指向pr,p的左标志为1;
b.若pr不为空且pr没有右孩子,置pr的右指针指向p,pr的右标志为1;(右线索的定义可能有点难以理解,可以画图来辅助理解)
置pr为p,p为栈顶的右孩子,栈顶退栈,返回2);
4)若栈为空且p为空,即遍历结束,置pr的右指针指向p,pr的右标志为1,结束。(最后的p指向空,即尾结点右线索为空)
2.代码实现
// 中序线索化
void thread(ThrTree t)
{
PSeqStack s = createEmptyStack(M);
PThrTreeNode p,pr=NULL; // pr用于指向p的前驱结点
p = t;
if(t == NULL) return;
do
{
while(p != NULL)
{
push(s, p);
p = p->llink; // 置p为p的左孩子,边走边进栈
}
p = top(s); // 取栈顶
if(p->llink == NULL) // p没有左孩子
{
p->ltag = 1; // p的左线索为中序前驱结点
p->llink = pr;
}
if(pr != NULL && pr->rlink == NULL) // pr没有右孩子且不为空
{
pr->rlink = p; // pr的右线索为中序后继结点
pr->rtag = 1;
}
pr = p;
p = p->rlink; // 置p为p的右孩子,继续向左下遍历
pop(s); // 栈顶处理完毕,退栈
}while(!isEmptyStack(s) || p != NULL);
pr->rlink = p; // 尾结点后继为NULL
pr->rtag = 1;
return;
}
// 中序遍历,线索二叉树的中序遍历不需要栈的借助
void InOrder(ThrTree t)
{
PThrTreeNode p=t;
if(t == NULL) return;
while(p->llink != NULL && p->ltag == 0)
{
p = p->llink; // 让p走到首结点
}
while(p != NULL)
{
visit(p);
if(p->rtag == 1) // 右指针是线索
{
p = p->rlink;
}
else if(p->rtag == 0) // 当p有右孩子时,它的后继是右孩子的最左下角的结点
{
p = p->rlink; // 置p为右孩子,走到右孩子第一个没有左孩子的结点
while(p->ltag == 0)
{
p = p->llink;
}
}
}
return;
}
int main(void)
{
ThrTree t;
printf("请输入该二叉树的扩充二叉树的先序序列:\n");
t = create_ThrTree();
thread(t); // 中序线索化
printf("中序遍历:\n");
InOrder(t); // 中序遍历
return 0;
}
测试结果: