2021 / 08 / 14 2021/08/14 2021/08/14
线索二叉树
\qquad
对于一棵二叉树我们可以进行前序、中序、后序及层序遍历,从而得到遍历序列,对于遍历序列中的每个结点(除第一个和最后一个外)都有直接前驱和直接后继。
也就是说:前驱后继是对应于具体遍历序列而言的
\qquad
传统的二叉树,通过lchild,rchild仅能体现出结点之间的父子关系,不能得到某个结点在遍历中的前驱或后继。
而我们已知,
n
n
n 个结点的二叉树,有
2
n
2n
2n 个链域,其中
n
+
1
n+1
n+1 个为空。
n
=
n
0
+
n
1
+
n
2
=
n
1
+
2
n
2
+
1
n=n_0+n_1+n_2=n_1+2n_2+1
n=n0+n1+n2=n1+2n2+1 所以
n
0
=
n
2
+
1
n_0=n_2+1
n0=n2+1
空指针有
n
1
+
2
n
0
=
n
1
+
n
0
+
n
2
+
1
=
n
+
1
n_1+2n_0=n_1+n_0+n_2+1=n+1
n1+2n0=n1+n0+n2+1=n+1 个
\qquad
我们利用这
n
+
1
n+1
n+1个空指针来指向前驱或后继,就得到了线索二叉树。
规定:
若无左子树,让lchild
指向其前驱结点;若无右子树,让rchild
指向其后继结点。
利用ltag、rtag来区分指向。
lchild | ltag | data | rtag | rchild |
---|
l t a g = { 0 l c h i l d 指 向 结 点 的 左 子 1 l c h i l d 指 向 结 点 的 前 驱 ltag=\begin{cases} 0 & lchild指向结点的左子 \\ 1 & lchild指向结点的前驱 \\ \end{cases} ltag={01lchild指向结点的左子lchild指向结点的前驱
r t a g = { 0 r c h i l d 指 向 结 点 的 右 子 1 r c h i l d 指 向 结 点 的 后 继 rtag=\begin{cases} 0 & rchild指向结点的右子\\ 1 & rchild指向结点的后继 \end{cases} rtag={01rchild指向结点的右子rchild指向结点的后继
线索二叉树的存储结构描述
typedef struct ThreadNode {
char data;
ThreadNode* lchild, * rchild;
int ltag = 0, rtag = 0; //初始化为0
}
二叉树的线索化
我们先通过普通方法得到一棵二叉树,然后通过线索化将其中的空指针指向其前驱或后继。实际相当于遍历了一遍二叉树。
中序线索二叉树的构造
对于中序线索二叉树,我们需要两个指针pre,p
,其中pre指向前一个结点,p指向正在访问的结点。
若p->lchild == NULL
,就让p->lcihld = pre
。 (指向其前驱)
若pre->rchild == NULL
,就让pre->rchild = p
。(指向其后继)
//中序线索化
void InThread(ThreadTree& p, ThreadTree& pre) { //pre为刚刚访问过的结点,p为正在访问的结点,pre为p前驱
if (p != NULL) {
InThread(p->lchild, pre); //线索化左子树
if (p->lchild == NULL) { //左子树为空,lchild指向其前驱
p->lchild = pre;
p->ltag = 1;
}
if (pre->rchild == NULL && pre != NULL) {
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
InThread(p->rchild, pre); //线索化右子树
}
}
//中序线索化构造
void CreateInThread(ThreadTree T) {
ThreadTree pre = NULL;
if (T != NULL) {
InThread(T, pre);
pre->rchild = NULL; //处理最后一个结点
pre->rtag = 1;
}
}
中序线索二叉树的遍历
先找到中序线索二叉树的第一个结点,然后依次找结点的后继,直至后继为空。
(1)找中序序列下的第一个结点
ThreadNode* Firstnode(ThreadNode* p) {
while (p->ltag == 0)
p = p->lchild;
return p;
}
(2)找到结点p在中序序列下的后继
ThreadNode* Nextnode(ThreadNode* p) {
if (p->rtag == 0)
return Firstnode(p->rchild);
else
return p->rchild;
}
(3)不含头结点的中序线索二叉树的遍历
void InOrderT(ThreadNode* T) {
for (ThreadNode* p = Firstnode(T); p != NULL; p = Nextnode(p))
cout << p->data;
}
题
二叉树线索化后仍然无法解决的问题:
先序线索二叉树——求前驱
后序线索二叉树——求后继
遍历仍然需要栈的是——后序线索二叉树