何为二叉线索树?
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化
typedef int TDataType;
typedef struct TreeNode {
TDataType val;
int ltag, rtag;
struct TreeNode* left, * right;
}TreeNode;
//注意:二叉搜索树与普通二叉树的区别在于结构体内多了两个成员ltag和rtag
//ltag=0,则left指向左孩子
//ltag=1,则left指向前驱节点
//rtag=0,则right指向右孩子
//rtag=1,则right指向后继节点
提示:二叉线索树的创建其实和正常二叉树的创建一样,只是多了一个线索化的过程。
二叉线索树一共有三种,分别以先序、中序、后序得到排列顺序为依据,得到节点的前驱和后继
以中序遍历为例子,图如下
void Visit(TreeNode* root,TreeNode**prev)
{
//root代表当前节点,*prev代表遍历root之前的一个节点
//指针指向前驱和后继的条件是为空
if (root->left == NULL)
{
root->left = *prev;//让当前节点的左指针指向前驱
root->ltag = 1;
}
if (*prev != NULL && (*prev)->right == NULL)
{
(*prev)->right = root;//让前一个指针的右节点指向它的后继
(*prev)->rtag = 1;
}
*prev = root;
}
//中序线索化二叉树
void InCreateT(TreeNode*root,TreeNode**prev)
{
if (root == NULL)
return;
//if (root->ltag == 0)//可加可不加---因为我们在改变完root之前就已经遍历过它了
InCreateT(root->left, prev);
Visit(root, prev);//只能改变root之前的节点的右指针,和root的左指针,不会改变root的右指针
//if (root->rtag == 0)//可加可不加---因为root的右指针没有被改变
InCreateT(root->right, prev);
}
void InThread(TreeNode* root)
{
if (root)
{
TreeNode* prev = NULL;
InCreateT(root, &prev);
//结束时,最后一个遍历的节点右指针是没有被判断是否该被初始化的,故在结束时要另行判断
if (prev->right == NULL)
prev->rtag = 1;
}
}
之后的先序线索二叉树,后序线索二叉树的思路与中序一样
先序代码如下
void Visit(TreeNode* root,TreeNode**prev)
{
//root代表当前节点,*prev代表遍历root之前的一个节点
//指针指向前驱和后继的条件是为空
if (root->left == NULL)
{
root->left = *prev;//让当前节点的左指针指向前驱
root->ltag = 1;
}
if (*prev != NULL && (*prev)->right == NULL)
{
(*prev)->right = root;//让前一个指针的右节点指向它的后继
(*prev)->rtag = 1;
}
*prev = root;
}
void PreCreateT(TreeNode*root,TreeNode**prev)
{
if (root == NULL)
return;
Visit(root,prev);//只能改变root之前的节点的右指针,和root的左指针,不会改变root的右指针
//所以左边要判断
if (root->ltag == 0)
PreCreateT(root->left, prev);
//当该节点的父节点的右孩子为空时,就会被改变指向它的后继,也就是当前节点,很显然会形成环,要判断
if (root->rtag == 0)
PreCreateT(root->right, prev);
}
void PreThread(TreeNode* root)
{
if (root)
{
TreeNode* prev = NULL;
PreCreateT(root,&prev);
if (prev->right == NULL)
prev->rtag = 1;
}
}
后序代码如下
void Visit(TreeNode* root,TreeNode**prev)
{
//root代表当前节点,*prev代表遍历root之前的一个节点
//指针指向前驱和后继的条件是为空
if (root->left == NULL)
{
root->left = *prev;//让当前节点的左指针指向前驱
root->ltag = 1;
}
if (*prev != NULL && (*prev)->right == NULL)
{
(*prev)->right = root;//让前一个指针的右节点指向它的后继
(*prev)->rtag = 1;
}
*prev = root;
}
void PostCreateT(TreeNode* root, TreeNode** prev)
{
if (root == NULL)
return;
//if (root->ltag == 0)//可加可不加---因为我们在改变完root之前就已经遍历过它了
PostCreateT(root->left, prev);
//if (root->rtag == 0)//可加可不加---因为我们在改变完root之前就已经遍历过它了
PostCreateT(root->right, prev);
Visit(root, prev);//只能改变root之前的节点的右指针,和root的左指针,不会改变root的右指针
}
void PostThread(TreeNode* root)
{
if (root)
{
TreeNode* prev = NULL;
PostCreateT(root, &prev);
if (prev->right == NULL)
prev->rtag = 1;
}
}
它们之间的区别其实只是先序遍历,中序遍历,后序遍历之间的区别,其他的代码几乎一摸一样(如果分不清楚什么时候要判断ltag和rtag,就全部判断就行)
附加用先序创建二叉线索树:
TreeNode* BuyTNode(TDataType x)
{
TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
if (newnode == NULL)
{
printf("malloc fail\n");
return NULL;
}
newnode->val = x;
newnode->ltag = newnode->rtag = 0;
newnode->left = newnode->right = NULL;
return newnode;
}
//用先序遍历创建二叉树
void CreateT(TreeNode** root)
{
TDataType x;
scanf("%d", &x);
if (x == 0)//这里假设0代表空节点,没有啥实际意义,跟根据具体情况修改
return;
else
{
*root = BuyTNode(x);
CreateT(&((*root)->left));
CreateT(&((*root)->right));
}
}