一、線索二叉樹的定義
在採用二叉樹鏈表做存儲結構時,二叉樹中的所有節點共有n+1個空指針域。因此可以利用二叉樹的二叉樹鏈表存儲結構中的那些空指針域來指示節點在某種遍歷序列中直接前驅和直接後繼的位置信息。這些指向直接前驅節點和直接後繼節點的指針被稱爲線索,加了線索的二叉樹成爲線索二叉樹。對二叉樹以某種次序遍歷使其變爲線索二叉樹的過程叫做線索化。
二、線索二叉樹的結構
1、爲每個節點增設兩個標誌位域ltag和rtag,令:
ltag = 0 //lchild指向節點的左孩子
ltag = 1 //lchild指向節點的前驅節點
rtag = 0 //rchild指向節點的右孩子
rtag = 1 //rchild指向節點的後繼節點
定義線索二叉樹的數據結構:
/*
*定義線索二叉樹的數據結構
*/
typedef char elemtype ;
typedef struct BiThrNode
{
elemtype data ;
struct BiThrNode *lchild , *rchild ; //定義指向左右孩子的指針
unsigned ltag : 1 ;
unsigned rtag : 1 ; //定義是否是前驅後後續節點
} BiThrNodeType , *BiThrTree ;
BiThrTree pre ; //定義一個全局變量指向線索二叉樹的前驅節點
三、建立線索二叉樹
1、二叉樹的線索化,實質上就是遍歷一棵二叉樹,在遍歷過程中,訪問節點的操作是檢查當前結點的左右指針域是否爲空,如果爲空,即將他們改爲前驅節點或後繼節點的線索。爲記錄前驅節點,定義pre爲全局變量,始終指向當前結點的前驅節點。
下面爲建立中序線索二叉樹的遞歸算法:
BiThrTree pre ; //定義一個全局變量指向線索二叉樹的前驅節點
/*
*中序遍歷進行線索化
*/
void InThreading(BiThrTree p)
{
if(p)
{
InThreading(p->lchild) ; //將左孩子線索化
if(!p->lchild)
{
p->ltag = 1 ;
p->lchild = pre ;
}
if(!pre->rchild)
{
pre->rtag = 1 ;
pre->rchild = p ;
}
pre = p ;
InThreading(p->rchild) ; //將右孩子線索化
}
}
/*
*建立頭節點,二叉樹線索化
*/
int InOrderThr(BiThrTree *head , BiThrTree T)
{
if(!((*head) = (BiThrTree)malloc(sizeof(BiThrNodeType))))
{
return 0 ;
}
(*head)->ltag = 0 ;
(*head)->rtag = 1 ;
(*head)->rchild = (*head) ; //頭指針回指
if(!T)
{
(*head)->lchild = *head ;
}
else
{
(*head)->lchild = T ;
pre = (*head) ;
InThreading(T) ;
pre->rchild = *head ;
pre->rtag = 1 ;
(*head)->rchild = pre ; //將最後一個節點線索化
}
return 1 ;
}
四、在中序線索二叉樹上查找任意節點的中序前驅節點
對於中序線索二叉樹上的任意節點,尋找其中序的前驅節點,有以下兩種情況:
1、如果該節點的左標誌域爲1,那麼其左指針所指向的節點便是它的前驅節點。
2、如果該節點的左標誌爲0,表明該節點有左孩子,根據中序遍歷的定義,它的前驅節點是以該節點的左孩子爲根節點的子樹的最右節點,即沿着其左子樹的右指針鏈向下查找,當某節點的右標誌域爲1時,它就是所要找的前驅節點。
算法如下:
/*
*在中序二叉樹上尋找任意節點的前驅節點
*/
BiThrTree InPreNode(BiThrTree p)
{
BiThrTree pre ;
pre = p->lchild ;
if(p->ltag != 1)
{
while(pre->rtag == 0)
{
pre = pre->rchild ;
}
}
return pre ;
}
五、在中序線索二叉樹上查找任意節點的中序後繼節點
對於中序線索二叉樹上的任意節點,尋找其中序的後繼節點,有以下兩種情況:
1、如果該節點的右標誌域爲1,那麼其右指針所指向的節點便是它的後繼節點。
2、如果該節點的右標誌爲0,表明該節點有右孩子,根據中序遍歷的定義,它的後繼節點是以該節點的右孩子爲根節點的子樹的最左節點,即沿着其右子樹的左指針鏈向下查找,當某節點的左標誌域爲1時,它就是所要找的後繼節點。
算法如下:
/*
*在中序二叉樹上尋找任意節點的後繼節點
*/
BiThrTree InPostNode(BiThrTree p)
{
BiThrTree post ;
post = p->rchild ;
if(p->rtag != 1)
{
while(post->rtag == 0)
{
post = post->lchild ;
}
}
return post ;
}
六、通過前驅節點與後繼節點進行線索二叉樹的遍歷
1、從最後一個節點開始訪問:
/*
*根據前驅節點進行中序線索二叉樹的遍歷
*/
void InOrderPre(BiThrTree head)
{
BiThrTree p ;
p = head->rchild ; //指向最後一個節點
while(p != NULL && p != head)
{
printf("%c " , p->data) ;
p = InPreNode(p) ;
}
}
2、從第一個節點開始遍歷:
/*
*根據後繼節點進行中序線索二叉樹的遍歷
*/
void InOrderPost(BiThrTree head)
{
BiThrTree p ;
p = head->lchild ;
while(p->ltag != 1)
{
p = p->lchild ;
}
while(p != NULL && p != head)
{
printf("%c " , p->data) ;
p = InPostNode(p) ;
}
}
七、測試的全部代碼
/*
*建立線索二叉樹,並測試一般性操作
*/
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MAXSIZE 100
/*
*定義線索二叉樹的數據結構
*/
typedef char elemtype ;
typedef struct BiThrNode
{
elemtype data ;
struct BiThrNode *lchild , *rchild ; //定義指向左右孩子的指針
unsigned ltag : 1 ;
unsigned rtag : 1 ; //定義是否是前驅後後續節點
} BiThrNodeType , *BiThrTree ;
BiThrTree pre ; //定義一個全局變量指向線索二叉樹的前驅節點
/*
*中序遍歷進行線索化
*/
void InThreading(BiThrTree p)
{
if(p)
{
InThreading(p->lchild) ; //將左孩子線索化
if(!p->lchild)
{
p->ltag = 1 ;
p->lchild = pre ;
}
if(!pre->rchild)
{
pre->rtag = 1 ;
pre->rchild = p ;
}
pre = p ;
InThreading(p->rchild) ; //將右孩子線索化
}
}
/*
*建立頭節點,二叉樹線索化
*/
int InOrderThr(BiThrTree *head , BiThrTree T)
{
if(!((*head) = (BiThrTree)malloc(sizeof(BiThrNodeType))))
{
return 0 ;
}
(*head)->ltag = 0 ;
(*head)->rtag = 1 ;
(*head)->rchild = (*head) ; //頭指針回指
if(!T)
{
(*head)->lchild = *head ;
}
else
{
(*head)->lchild = T ;
pre = (*head) ;
InThreading(T) ;
pre->rchild = *head ;
pre->rtag = 1 ;
(*head)->rchild = pre ; //將最後一個節點線索化
}
return 1 ;
}
/*
*在中序二叉樹上尋找任意節點的前驅節點
*/
BiThrTree InPreNode(BiThrTree p)
{
BiThrTree pre ;
pre = p->lchild ;
if(p->ltag != 1)
{
while(pre->rtag == 0)
{
pre = pre->rchild ;
}
}
return pre ;
}
/*
*在中序二叉樹上尋找任意節點的後繼節點
*/
BiThrTree InPostNode(BiThrTree p)
{
BiThrTree post ;
post = p->rchild ;
if(p->rtag != 1)
{
while(post->rtag == 0)
{
post = post->lchild ;
}
}
return post ;
}
void PreInO(char preOrder[] , char inOrder[] , int i , int j , int k , int h , BiThrTree *bt)
{
int m ;
if(!((*bt) = (BiThrTree)malloc(sizeof(BiThrNodeType))))
{
return ;
}
else
{
(*bt)->data = preOrder[i] ;
m = k ;
while(preOrder[i] != inOrder[m])
{
m++ ;
}
if(m == k)
{
(*bt)->lchild = NULL ;
}
else
{
PreInO(preOrder , inOrder , i + 1 , m - k + i , k , m - 1 , &((*bt)->lchild)) ; //進行左子樹的建立
}
if(m == h)
{
(*bt)->rchild = NULL ;
}
else
{
PreInO(preOrder , inOrder , m - k + i + 1 , j , m + 1 , h , &((*bt)->rchild)) ; //進行右子樹的建立
}
}
}
/*
*根據二叉樹的前序遍歷序列和中序遍歷確定唯一二叉樹
*根據二叉樹的中序遍歷和後續遍歷不能確定唯一二叉樹
*/
void ReBiTree(char preOrder[] , char inOrder[] , BiThrTree *bt)
{
int len ;
len = strlen(preOrder) ;
if(len <= 0)
{
return ;
}
else
{
PreInO(preOrder , inOrder , 0 , len - 1 , 0 , len - 1 , bt) ;
}
}
/*
*根據前驅節點進行中序線索二叉樹的遍歷
*/
void InOrderPre(BiThrTree head)
{
BiThrTree p ;
p = head->rchild ; //指向最後一個節點
while(p != NULL && p != head)
{
printf("%c " , p->data) ;
p = InPreNode(p) ;
}
}
/*
*根據後繼節點進行中序線索二叉樹的遍歷
*/
void InOrderPost(BiThrTree head)
{
BiThrTree p ;
p = head->lchild ;
while(p->ltag != 1)
{
p = p->lchild ;
}
while(p != NULL && p != head)
{
printf("%c " , p->data) ;
p = InPostNode(p) ;
}
}
/*
*進行線索二叉樹的測試
*/
void main()
{
BiThrTree head , T ;
char pre[MAXSIZE] , inOrder[MAXSIZE] ;
scanf("%s" , pre) ;
scanf("%s" , inOrder) ;
ReBiTree(pre , inOrder , &T) ; //建立普通二叉樹
InOrderThr(&head , T) ; //建立頭節點,並將二叉樹線索化
InOrderPre(head) ;
printf("\n") ;
InOrderPost(head) ;
}