用ABDEC举个例子,后序线索二叉树构建的原理与过程:
后序序列为DEBCA,结点D无左孩子,也无前驱故置空,无右孩子,将右链域指向后继E;
结点E无左孩子,将左链域指向前驱D,无右孩子,将右链域指向后继B;
结点C无左孩子,将左链域指向前驱B,无右孩子,将右链域指向后继A,得到的后序线索二叉树如下图所示。
红色箭头代表左右孩子,黄色箭头代表前驱与后继,共有n个结点,因此左右孩子指针的前驱,后继指针一共有2n-1个。
1.定义结构体
typedef struct TreeNode
{
char data;//存放数据
struct TreeNode *lchild;//左孩子
struct TreeNode *rchild;//右孩子
struct TreeNode *parent;//父结点
int ltag;//左标志
int rtag;//右标志
}TreeNode;
2.创建二叉树
/*创建后序线索二叉树*/
void createTree(TreeNode **T,char *data,int *index,TreeNode *parent)
{/*由于要改变T中的值,因此需要双重解引用
第一个参数:代表树中的结点
第二个参数:代表输入的字符串,也就是树中每个结点的数值
第三个参数:代表data字符串的元素下标,也就是每个元素的位置,从0开始
第四个参数:父节点*/
char ch;
ch = data[*index];//按照顺序将data字符串的每个元素取出
*index += 1;//下标加一
if(ch=='#')//#代表指向空,如果元素是#,则T直接置为空,如果不是进入下个步骤
*T = NULL;
else if(ch!='\0')
{
*T = (TreeNode *)malloc(sizeof(TreeNode));//为T申请新的内存空间
(*T)->data = ch;//将该元素写入T结点中
(*T)->ltag = 0;//左标记初始化为0
(*T)->rtag = 0;//右标记初始化为0
(*T)->parent = parent;//左右孩子的父结点写入
/*&((*T)->lchild)表示:取*T指向的左孩子的地址*/
createTree(&((*T)->lchild),data,index,*T);//递归,左子树,处理下一个字符
createTree(&((*T)->rchild),data,index,*T);//递归,右子树,处理下一个字符
}
}
3.线索化二叉树
void postThreadTree(TreeNode *T,TreeNode **pre)
{/*第一个参数*T:表示二叉树中的结点
第二个参数**pre:指向前一个结点,初值为NULL,由于pre要改变,因此使用双重解引用(也可设置static变量代替)*/
if(T)//T不为空进入下个步骤
{
postThreadTree(T->lchild,pre);//递归,线索左子树
postThreadTree(T->rchild,pre);//递归,线索右子树
if(T->lchild==NULL)//左孩子为空则有前驱
{
T->ltag = 1;//左标志置为1
T->lchild = *pre;//T指向前驱结点pre
}
if(*pre!=NULL&&(*pre)->rchild==NULL)//前驱不为空且前驱的右孩子为空,则有后继
{
(*pre)->rtag = 1;//前驱的右标志置为1
(*pre)->rchild = T;//前驱的右孩子(后继)指向T
}
*pre = T;//更新pre的值
}
}
4.求子树的第一个结点
TreeNode *getFirst(TreeNode *T)
{/*后序线索二叉树的第一个结点一定是最底层最左边的结点(叶子节点)*/
while(T->ltag==0)//表示有左孩子,遍历左孩子
T = T->lchild;
if(T->rtag==0)//有右孩子,遍历右子树
return getFirst(T->rchild);//递归遍历右子树
return T;
}
5.后序遍历线索二叉树
TreeNode *getNext(TreeNode *node)
{
if(node->rtag==1)//说明已经线索化,该结点一定没有右孩子
return node->rchild;//指向后继
else
{
if(node->parent==NULL)//该结点为根结点
return;
else if(node->parent->rchild==node)//该结点为右孩子
return node->parent;
else
{
if(node->parent->ltag==0)//父结点有右孩子
return getFirst(node->parent->rchild);//找到以node的父结点为首的第一个结点
else
return node->parent;//该结点为左孩子,且没有右兄弟,直接返回
}
}
}
6.主函数验证
int main()
{
char data[11];//ABD##E##C##,要验证的字符串一共11个字符
char ch;
gets(data);
TreeNode *T;//定义根
TreeNode *pre = NULL;//定义前驱
int index = 0;
createTree(&T,data,&index,NULL);//验证创建二叉树算法
postThreadTree(T,&pre);//验证线索化二叉树算法
TreeNode *node;
for(node=getFirst(T);node!=NULL;node=getNext(node))//循环打印输出
printf("%c ",node->data);
printf("\n");
return 0;
}
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
/*定义结构体*/
typedef struct TreeNode
{
char data;//存放数据
struct TreeNode *lchild;//左孩子
struct TreeNode *rchild;//右孩子
struct TreeNode *parent;//父结点
int ltag;//左标志
int rtag;//右标志
}TreeNode;
/*创建二叉树*/
void createTree(TreeNode **T,char *data,int *index,TreeNode *parent)
{/*由于要改变T中的值,因此需要双重解引用
第一个参数:代表树中的结点
第二个参数:代表输入的字符串,也就是树中每个结点的数值
第三个参数:代表data字符串的元素下标,也就是每个元素的位置,从0开始
第四个参数:父节点*/
char ch;
ch = data[*index];//按照顺序将data字符串的每个元素取出
*index += 1;//下标加一
if(ch=='#')//#代表指向空,如果元素是#,则T直接置为空,如果不是进入下个步骤
*T = NULL;
else if(ch!='\0')
{
*T = (TreeNode *)malloc(sizeof(TreeNode));//为T申请新的内存空间
(*T)->data = ch;//将该元素写入T结点中
(*T)->ltag = 0;//左标记初始化为0
(*T)->rtag = 0;//右标记初始化为0
(*T)->parent = parent;//左右孩子的父结点写入
/*&((*T)->lchild)表示:取*T指向的左孩子的地址*/
createTree(&((*T)->lchild),data,index,*T);//递归,左子树,处理下一个字符
createTree(&((*T)->rchild),data,index,*T);//递归,右子树,处理下一个字符
}
}
/*线索化二叉树*/
void postThreadTree(TreeNode *T,TreeNode **pre)
{/*第一个参数*T:表示二叉树中的结点
第二个参数**pre:指向前一个结点,初值为NULL,由于pre要改变,因此使用双重解引用(也可设置static变量代替)*/
if(T)//T不为空进入下个步骤
{
postThreadTree(T->lchild,pre);//递归,线索左子树
postThreadTree(T->rchild,pre);//递归,线索右子树
if(T->lchild==NULL)//左孩子为空则有前驱
{
T->ltag = 1;//左标志置为1
T->lchild = *pre;//T指向前驱结点pre
}
if(*pre!=NULL&&(*pre)->rchild==NULL)//前驱不为空且前驱的右孩子为空,则有后继
{
(*pre)->rtag = 1;//前驱的右标志置为1
(*pre)->rchild = T;//前驱的右孩子(后继)指向T
}
*pre = T;//更新pre的值
}
}
/*求子树的第一个结点*/
TreeNode *getFirst(TreeNode *T)
{/*后序线索二叉树的第一个结点一定是最底层最左边的结点(叶子节点)*/
while(T->ltag==0)//表示有左孩子,遍历左孩子
T = T->lchild;
if(T->rtag==0)//有右孩子,遍历右子树
return getFirst(T->rchild);//递归遍历右子树
return T;
}
/*后序遍历线索二叉树*/
TreeNode *getNext(TreeNode *node)
{
if(node->rtag==1)//说明已经线索化,该结点一定没有右孩子
return node->rchild;//指向后继
else
{
if(node->parent==NULL)//该结点为根结点
return;
else if(node->parent->rchild==node)//该结点为右孩子
return node->parent;
else
{
if(node->parent->ltag==0)//父结点有右孩子
return getFirst(node->parent->rchild);//找到以node的父结点为首的第一个结点
else
return node->parent;//该结点为左孩子,且没有右兄弟,直接返回
}
}
}
int main()
{
char data[11];//ABD##E##C##,要验证的字符串一共11个字符
char ch;
gets(data);
TreeNode *T;//定义根
TreeNode *pre = NULL;//定义前驱
int index = 0;
createTree(&T,data,&index,NULL);//验证创建二叉树算法
postThreadTree(T,&pre);//验证线索化二叉树算法
TreeNode *node;
for(node=getFirst(T);node!=NULL;node=getNext(node))//循环打印输出
printf("%c ",node->data);
printf("\n");
return 0;
}