线索二叉树
对于节点个数为n的二叉树,采用二叉链存储结构时,每个节点有2个指针域,总共有2n个指针域,其中只有n-1个分支,所以有2n-(n-1),n+1个空指针。这些空指针不存储任何信息,白白浪费了内存空间。
在介绍树的链式存储结构的缺点时,我们提到:
寻找一个孩子节点的双亲是比较麻烦的,要用递归函数来遍历整棵树。
对于一棵经常需要遍历或查找节点的二叉树,如何提高操作的时间效率呢?既然有那么多空指针,我们可以在第一次遍历二叉树时把空指针利用起来,存放前驱节点和后继节点的地址。我们把指向前趋和后继节点的指针称为线索。
创建线索的过程称为线索化,一棵线索化的二叉树称为线索二叉树(Threader Binary Tree)。
同一棵二叉树用不同的遍历方法会产生不一样的线索二叉树
比如:
- 先序线索二叉树
- 中序线索二叉树
- 后序线索二叉树

线索二叉树节点
typedef struct _tbtnode{
	  ElemType data;
	  int ltag, rtag;
	  struct _tbtnode *lchild, *rchild;
}tbtnode;
为了区分线索和分支,在原有二叉树节点的基础上增加标记ltag和rtag。
| 0 | 1 | |
|---|---|---|
| ltag | 指向左孩子节点 | 指向遍历序列的后继节点 | 
| rtag | 指向右孩子节点 | 指向遍历序列的前驱节点 | 
建立线索二叉树
建立某种次序的线索二叉树
- 以该遍历方法遍历一棵二叉树
- 在遍历过程中,检查当前访问节点的左孩子指针、右孩子指针是否为空
- 如果左孩子指针为空,则改为指向前驱节点的线索
- 如果右孩子指针为空,则改为指向后继节点的线索
以中序线索二叉树为例,讨论建立线索二叉树的算法
对于中序遍历线索化的递归函数:
- p总是指向当前线索化的节点
- pre作为全局变量,指向刚刚访问过的节点
- *pre是- *p的中序前驱节点,- *p是- *pre的中序后继节点
btnode* pre;       //全局变量
void
Thread(tbtnode* p)
{
    if (p != NULL) {
        Thread(p->lchild);		//中序遍历先遍历左子树
        if (p->lchild == NULL) {
            p->ltag = 1;        //设置线索标记
            p->lchild = pre;    //指向当前节点的前驱节点
        }
        else {
            p->ltag = 0;        //设置孩子指针标记
        }
        if (pre->rchild == NULL) {
            pre->rtag = 1;      
            pre->rchild = p;    //前一个节点指向当前节点,即让指针指向后继节点
        }
        else {
            pre->rtag = 0;
        }
        pre = p;                //pre和p向前移动
        Thread(p->rchild);		//中序遍历后遍历右子树
    }		
}

在遍历线索二叉树时我们发现,这棵树实际上可以看成是一个双向链表,和双链表一样,给线索二叉树增加头节点,这样一来我们既可以从第一个节点起沿着后继遍历,也可以从最后一个节点沿着前驱遍历。
 
tbtnode*
createThread(tbtnode* t)
{
    tbtnode* root;
    root = (tbtnode*)malloc(sizeof(tbtnode));
    root->ltag = 0;
    root->rtag = 1;
    root->rchild = t;   //头结点的右孩子指针开始时指向树的根节点
    if (t == NULL) {
        root->lchild = root;
    }
    else {
        root->lchild = t;
        pre = root;
        Thread(t);
        pre->rchild = root;
        /*
        * 离开Thread()时p为空指针, pre指向了中序遍历序列的最后一个节点
        * 让最后一个节点的右孩子指针指向后继节点root
        */
        pre->rtag = 1;
        root->rchild = pre;
    }
    return root;
}
遍历线索二叉树
用非递归算法即可遍历线索二叉树,空间复杂度为O(1)
void
InOrderTraverse_Thr(tbtnode* t)
{
    tbtnode* p = t->lchild;     //p指向根节点
    while (p != t) {
        while (p->ltag == 0) {  //遍历左子树找到开始节点
            p = p->lchild;
        }
        printf("%c", p->data);  //打印开始节点
        while (p->rtag == 1 && p->rchild != t) {    //沿着线索一路访问下去
            p = p->rchild;
            printf("%c", p->data);
        }
        p = p->rchild;  //p指向下一棵子树
    }
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号