二叉树,二叉链表中,有N个结点,2N个指针,N+1个空指针,把空指针加以利用,可以线索结点的前驱或后继,这就形成了线索二叉树;无左孩子的空指针,指向该结点前驱,无右孩子的空指针,指向该结点的后继;这样,就可以不用递归或栈就可以遍历二叉树;
1、线索二叉树的概念,指向遍历序列前驱、后继结点的指针称为线索;把空指针修改为线索的过程称为线索化;经过线索化的二叉树称为线索二叉树;
2、线索二叉树的结构,为了区分指针是否为线索,需要修改结点结构,增加左右标志域Ltag,Rtag用于判断;可以设定Ltag为0指示是结点的左孩子,为1指示是结点的前驱,Rtag为0指示是结点的右孩子,为1指示是结点的后继;
3、线索二叉树的种类,依据二叉树遍历的不同,存在三种不同的线索二叉树,先序线索二叉树,中序线索二叉树,后序线索二叉树;二叉树的线索化,是按三种不同遍历的序列进行;
4、二叉树线索化的实现,在遍历的过程中,设定二个指针,一个指向当前结点的指针p,一个指向当前结点的前驱结点的指针pre,p与pre是动态变化的一对指针,pre是p的前驱,p是pre的后继,这一点非常重要,判断当前结点(p指向)的左孩子指针是否为空,如为空,指针pre修改当前结点左孩子指针;判断前驱结点(pre指向)的右孩子指针是否为空,如为空,指针p修改前驱结点(pre指向)的右孩子指针;左右孩子指针为空,别忘了修改Ltag或Rtag为1;p判断左孩子,pre判断右孩子;
/* 中序遍历二叉树 线索化 */ BinTree pre = NULL; /* 定义全局变量pre指针 */ void InThread(BinTree p) { if(p != NULL) { InThread(p-Left); if(p->Left==NULL){ /* 当前结点的左孩子为空 */ p->Left = pre; /* pre修改当前结点左孩子指针 */ p->Ltag = 1; /* 左孩子指针为线索 */ } if(pre != NULL && pre->Right == NULL){ pre->Right = p; /* 当前结点的前驱结点的右孩子为空 */ pre->Rtag = 1; } pre = p; /* 更新pre */ InThread(p->Right); } }
5、遍历线索二叉树,依据不同的线索二叉树,访问遍历的第一个结点;寻找并访问当前结点的后继结点,直至所有的结点被访问;
中序遍历的第一个结点是树中处于最左下端的结点,从根沿着左孩子指针找到没有左孩子的结点即可;中序遍历的后继结点,结点若无右孩子,Right指针指向的是其后继,若有右孩子,右子树中最左下端结点即为其后继;在中序线索树中找结点的直接前驱,是找左子树中最右下端的结点,左子树为线索的可以直接找到;
/* 寻找中序遍历的第一个结点 */ BinTree InFirst(BinTree Bt) { BinTree p = Bt; if(!p) return NULL; while(p->Ltag == 0) p = p->Left; return p; }
1 /* 寻找中序遍历的当前结点的后继结点 */ 2 BinTree InNext(BinTree p) 3 { 4 BinTree Next = NULL; 5 if(p->Rtag == 1) Next = p->Right; 6 else{ 7 for(BinTree q = p->Right; q->Ltag == 0; q = q->Left); 8 Next = q; 9 } 10 return Next; 11 }
/* 中序线索二叉树的遍历算法 */ void InOrder(BinTree Bt) { BinTree p = NULL; p = InFirst(Bt); while(p) { visit(p); p = InNext(p); } }