线索二叉树

对于节点个数为n的二叉树,采用二叉链存储结构时,每个节点有2个指针域,总共有2n个指针域,其中只有n-1个分支,所以有2n-(n-1)n+1个空指针。这些空指针不存储任何信息,白白浪费了内存空间。

在介绍树的链式存储结构的缺点时,我们提到:

寻找一个孩子节点的双亲是比较麻烦的,要用递归函数来遍历整棵树。

对于一棵经常需要遍历或查找节点的二叉树,如何提高操作的时间效率呢?既然有那么多空指针,我们可以在第一次遍历二叉树时把空指针利用起来,存放前驱节点后继节点的地址。我们把指向前趋和后继节点的指针称为线索

创建线索的过程称为线索化,一棵线索化的二叉树称为线索二叉树(Threader Binary Tree)。

同一棵二叉树用不同的遍历方法会产生不一样的线索二叉树

比如:

  • 先序线索二叉树
  • 中序线索二叉树
  • 后序线索二叉树

线索二叉树_第1张图片

线索二叉树节点

typedef struct _tbtnode{
	  ElemType data;
	  int ltag, rtag;
	  struct _tbtnode *lchild, *rchild;
}tbtnode;

为了区分线索和分支,在原有二叉树节点的基础上增加标记ltagrtag

0 1
ltag 指向左孩子节点 指向遍历序列的后继节点
rtag 指向右孩子节点 指向遍历序列的前驱节点

建立线索二叉树

建立某种次序的线索二叉树

  1. 以该遍历方法遍历一棵二叉树
  2. 在遍历过程中,检查当前访问节点的左孩子指针、右孩子指针是否为空
  • 如果左孩子指针为空,则改为指向前驱节点的线索
  • 如果右孩子指针为空,则改为指向后继节点的线索

以中序线索二叉树为例,讨论建立线索二叉树的算法

对于中序遍历线索化的递归函数:

  • 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);		//中序遍历后遍历右子树
    }		
}

线索二叉树_第2张图片

在遍历线索二叉树时我们发现,这棵树实际上可以看成是一个双向链表,和双链表一样,给线索二叉树增加头节点,这样一来我们既可以从第一个节点起沿着后继遍历,也可以从最后一个节点沿着前驱遍历。
线索二叉树_第3张图片

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指向下一棵子树
    }
}

你可能感兴趣的:(数据结构)