一. 线索二叉树的基础
线索: 指向前驱和后继的指针
线索链表:加上线索的二叉链表
线索二叉树:加上线索的二叉树(相当于一个双向链表)
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程
线索化的过程就是在遍历中修改空指针的过程
如何知道rchild(lchild)是指向右(左)孩子还是后继呢?
设置两个标志域 ltag和rtag ,ltag和rtag是只存放0或1的布尔型变量:
- ltag为 0 时指向该结点的左孩子,为 1 时指向该结点的前驱
- rtag为 0 时指向该结点的右孩子,为 1 时指向该结点的后继
二. 结构实现
1. 普通线索二叉树
线索二叉树结构:
//Link = 0,表示左右孩子指针
//Thead = 1,表示前驱或后继
typedef enum { Link, Thread } Pointertag;
typedef struct BiThrTree
{
char data;
struct BiThrTree* lchild, * rchild;
Pointertag ltag;
Pointertag rtag;
}BiThrNode,*BiThrTree;
BiThrTree pre;//指向刚刚访问过的结点
线索二叉树中序线索化:
void Inthread(BiThrTree p)
{
if (p)
{
Inthread(p->lchild);
//若某结点的左指针域为空(前驱结点刚刚访问过,赋值给了pre)
if (!p->lchild)
{
p->ltag = Thread;
p->lchild = pre;
}
//P的后继还没访问到,因此对P的前驱pre的右指针进行判断
if (!pre->rchild)
{
pre->rtag = Thread;
pre->rchild = p;
}
pre = p; //将当前结点赋值给pre
Inthread(p->rchild);
}
}
- 这段代码只是将二叉树的中序遍历的递归代码本是打印结点的功能改成了线索化
线索二叉树中序遍历:
与中序方式线索化二叉树相对应
void Inorder(BiThrTree T)
{
//让p指向根结点
BiThrTree p = T;
while (p)
{
//遍历到中序遍历第一个开始的结点(左子树)
while (p->ltag == Link)
{
p = p->lchild;
}
printf("%c", p->data);
//如果该结点右孩子是线索(后继),且右指针存在,就一直遍历
while (p->rtag == Thread && p->rchild)
{
p = p->rchild;
printf("%c", p->data);
}
/*
* 当右孩子不是线索时(没有后继),
* 说明该结点右孩子存在,
* 所以接着右孩子的遍历
*/
p = p->rchild;//p进至其右子树根
}
}
2. 含头结点的线索二叉树
在二叉树根结点前增加一个头结点,其左孩子指向根结点,右孩子指向中序遍历的最后一个结点,相当于双向链表
线索二叉树中序线索化:
void Inthread2(BiThrTree* head, BiThrTree T)
{
(*head) = (BiThrTree)malloc(sizeof(BiThrNode));
//左孩子是根结点 0
(*head)->ltag = Link;
//右孩子是线索 1
(*head)->rtag = Thread;
//右孩子先指向自身,防止原二叉树为空
(*head)->rchild = (*head);
//二叉树为空
if (T == NULL)
{
//左孩子也指向自身
(*head)->lchild = *head;
return;
}
(*head)->lchild = T;
pre = *head; // 前指针指向头结点
/*
* 普通的中序线索化二叉树,
* 此时二叉树中序序列的第一个结点的左孩子指向头结点
* 结束后,此时的pre指向最后一个元素
*/
Inthread(T);
pre->rchild = *head;
pre->rtag = Link;
(*head)->rchild = pre;
}
既可以从头遍历,也可以从尾部遍历
线索二叉树中序遍历:
void Inorder2(BiThrTree head)
{
//让p指向根结点
BiThrTree p = head->lchild;
//空树或遍历结束 p==head
while (p != head)
{
while (p->ltag == Link)
{
p = p->lchild;
}
printf("%c", p->data);
//右孩子不能为 head
while (p->rtag == Thread && p->rchild!=head)
{
p = p->rchild;
printf("%c", p->data);
}
p = p->rchild;
}
}
当经常需要遍历或查找结点时,可以使用,它的时间复杂度远低于递归的方法