中序线索二叉树
中序线索二叉树
逻辑结构:
用空分支指向前驱和后继,如何线索化?
一个结点如果有空指针,则左空指针指向其遍历序列前驱,如果有右空指针指向遍历序列后继
typedef struct TBTNode {
int data;
int lTag;//0指向左孩子,1指向前驱
int rTag;//
TBTNode *lchild;
TBTNode *rchild;
}TBTNode;
中序线索化代码:
//pre为p的父结点/前驱
void inThread(TBTNode *p,TBTNode *&pre)
{
if(p != NULL)
{
inThread(p->lchild,pre);
//把p左孩子指向p的前驱
if(p->lchild == NULL)
{
p->lchild = pre;
p->lTag = 1;
}
//把p右孩子指向p的后继
//我们采用pre指向p来连接(为什么不用p来指向p的后继呢,因为我们没有p的后继这个变量)
if(pre != NULL && pre->rchild == NULL)
{
pre->rchild = p;
pre->rTag = 1;
}
//pre与p同步,那么p是如何再领先pre到下一步呢,
//当递归结束,p回溯,就实现了p走到pre的下一步了
pre = p;
inThread(p->rchild,pre);
}
}
中序线索二叉树:
开头:最左的左孩子
结尾:最右的右孩子
给一个结点如何找到它遍历的前驱和后继
找前驱
:从这个结点左走一步,再一直往右走到底。
比如:A1的前驱,左走一步A2,然后一直右走,是A5。
找后继
:从这个结点右走一步,再一直往左走。
比如:A1的后继,右走一步A3,再一直左走,是A6。
前序线索二叉树
逻辑结构相似,遍历方式不一样。
一个结点如果有空指针,则左空指针指向其遍历序列前驱,如果有右空指针指向遍历序列后继
//pre为p的父结点/前驱
void preThread(TBTNode *p,TBTNode *&pre)
{
if(p != NULL)
{
//把p左孩子指向p的前驱
if(p->lchild == NULL)
{
p->lchild = pre;
p->lTag = 1;
}
//把p右孩子指向p的后继
//我们采用pre指向p来连接(为什么不用p来指向p的后继呢,因为我们没有p的后继这个变量)
if(pre != NULL && pre->rchild == NULL)
{
pre->rchild = p;
pre->rTag = 1;
}
//pre与p同步,那么p是如何再领先pre到下一步呢,
//当递归结束,p回溯,就实现了p走到pre的下一步了
pre = p;
//左指针不是线索,才往左走,避免绕圈陷入死循环
if(p->lTag == 0)
preThread(p->lchild,pre);
if(p->rTag == 0)
preThread(p->rchild,pre);
}
}
如何在前序线索二叉树上执行前序遍历操作?
第一个结点是根结点。找后继结点,如果一个结点的左指针不空,并且左指针不是线索,那么左指针指向其后继(是线索的话就指向其前驱了)。如果一个结点其左指针空,其右指针一定指向指向其后继结点(普通和线索一样)
void preOrder(TBTNode *tbt)
{
if(tbt != NULL)
{
TBTNode *p = tbt;
while(p!=NULL)
{
//把左边找完
while(p->lTag == 0)
{
visit(p);
p = p->lChild;
}
//右指针指向的是后继
visit(p);
p = p->lChild;
}
}
}
后序线索二叉树
void postThread(TBTNode *p,TBTNode *&pre)
{
if(p != NULL)
{
postThread(p->lchild,pre);
postThread(p->rchild,pre);
if(p->lchild == NULL)
{
p->lchild = pre;
p->lTag = 1;
}
if(pre != NULL && pre->rchild == NULL)
{
pre->rchild = p;
pre->rTag = 1;
}
pre = p;
}
}
后序考点:
- 如果结点是二叉树的根,则其后继为空。
- 若结点是其双亲的右孩子;或结点是其双亲的左孩子且其双亲没有右子树;则其后继为双亲结点。
- 若结点是其双亲的左孩子,且其双亲有右子树,则其后继为双亲右子树上按后序遍历列出的第一个结点。(不要多想,要有大局观,它就是想说,从左子树跨到右子树了。)
代码的记法:
1.初始状态
初始:p = A1, pre = NULL
2.两个连接的if语句
if(p->lchild == NULL)
if(pre != NULL && pre->rchild == NULL)
两个if() 放xxxThread()前面叫前序,放中间叫中序,放最后叫后序.
3.只有前序前面需要加if(p->xTag == 0)来判断(避免形成环),中序不需要这个if。
//只有前序线索才需要检测是否有环
if(p->lTag == 0)
xxThread(p->lchild,pre);
if(p->rTag == 0)
xxThread(p->rchild,pre);
当两个if都放在inThread()前面就是前序线索二叉树的代码,两个if都放在两个inThread ()中间,就是中序线索二叉树的代码。
三种线索二叉树的比较
前序:找后继结点方便,但不方便找前驱结点
(比如:A2->A1,只知道A2如何找A1)
中序:前驱和后继都能很方便的找到。
找前驱
:从这个结点左走一步,再一直往右走到底。
找后继
:从这个结点右走一步,再一直往左走。
(在前面有)
后序:找前驱和找后序都复杂。(具体过程在前面的总结)
(所有找前驱,考研考的相当少,几乎不考,主要考的是找后继的方法)