通过中序遍历对二叉树线索化的算法实现
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
线索二叉树的结点结构:
ltag | lchild | data | rchild | rtag |
---|
其中,标志域的含义如下:
l
t
a
g
{
0
,
l
c
h
i
l
d
域
指
向
结
点
的
左
孩
子
1
,
l
c
h
i
l
d
域
指
向
结
点
的
前
驱
ltag\left\{ \begin{aligned} 0,lchild域指向结点的左孩子 \\ 1,lchild域指向结点的前驱 \end{aligned} \right.
ltag{0,lchild域指向结点的左孩子1,lchild域指向结点的前驱
r
t
a
g
{
0
,
r
c
h
i
l
d
域
指
向
结
点
的
右
孩
子
1
,
r
c
h
i
l
d
域
指
向
结
点
的
后
继
rtag\left\{ \begin{aligned} 0,rchild域指向结点的右孩子 \\ 1,rchild域指向结点的后继 \end{aligned} \right.
rtag{0,rchild域指向结点的右孩子1,rchild域指向结点的后继
线索二叉树存储结构描述如下:
typedef struct ThreadNode {
// 定义线索二叉树存储结构
ElemType data; // 数据域
struct ThreadNode *lchild, *rchild; // 左、右孩子指针
int ltag, rtag; // 左、右线索标志
}ThreadNode, *ThreadTree;
线索二叉树的构造:
对二叉树的线索化,实质就是遍历一次二叉树,只是在遍历的过程中,检查当前结点左、右指针域是否为空,若为空,将它们改为指向前驱结点或后继结点的线索。
递归算法
void InThread(ThreadTree p, ThreadTree* pre) {
// 中序遍历对二叉树线索化的递归算法
if (p) {
InThread(p->lchild, pre); // 递归,线索化左子树
if (!p->lchild) { // 左子树为空,则建立前驱线索
p->lchild = *pre;
p->ltag = 1;
}
if (*pre && !(*pre)->rchild) { // 建立前驱结点的后继线索
(*pre)->rchild = p;
(*pre)->rtag = 1;
}
*pre = p; // 标记当前结点成为刚刚访问过的结点
InThread(p->rchild, pre); // 递归,线索化右子树
}
}
void CreateInThread(ThreadTree T)
{// 通过中序遍历建立中序线索二叉树的主过程
ThreadTree pre = NULL;
if (T) { // 非空二叉树,线索化
InThread(T, &pre); // 线索化二叉树
pre->rchild = NULL; // 处理遍历的最后一个结点
pre->rtag = 1;
}
}
非递归算法
void InThread(ThreadTree T) {
// 中序遍历对二叉树线索化的非递归算法
SqStack S = {0}; S.top = -1;
ThreadTree p = T, pre = NULL;
while(p || S.top != -1) { // 根节点或栈非空时循环
if (p) { // 每遇到非空结点先入栈,再往左遍历
Push(&S, p);
p = p->lchild;
} else { // 退栈,访问根结点
Pop(&S, &p);
if (!p->lchild) { // 若左子树为空,则建立前驱线索
p->lchild = pre;
p->ltag = 1;
}
if (pre && !pre->rchild) { // 建立前驱结点的后继线索
pre->rchild = p;
pre->rtag = 1;
}
pre = p; // 标记当前结点成为刚刚访问过的结点
p = p->rchild; // 遍历右子树
}
}
}