上篇刚写了个普通的二叉树,现在讨论线索二叉树了,相关代码下载下载代码
关于线索二叉树,自己在学习的时候,总是感觉到头晕,主要原因在于总是搞不清楚这个前驱和后继是怎么联系起来的,其实这个我是把事情想得有点复杂了,人家都是复杂事情简单化,而我倒成了简单事情复杂化了,汗!
现在总结下这个前驱和后继是怎么回事,其实首先要理解的就是,学习线索前我们要联系前面学习的几种遍历方式,因为他们是息息相关的,且前驱和后继不一定总是有因为这还跟该树中的左右子树SubTree是否存在有关,如果存在则该前驱或者后继就不存在了,相反我们也不需要得到因为我们可以通过遍历来得到,例如: 如果要求一颗树的前驱,而该树的(假定左子树保存前驱指针,右子树保存后继指针,当然这里是假设子树不存在而标志域线索是的情况了),左子树存在,则如果按照中序遍历的方式即 左--中--右 方式,该树的前驱是左子树中沿着右子树一直往下查找直到找到的是是线索为止,这个似乎也比较难理解,我们出张图就清楚了,请看下图:
手画草图,但可说明意思,将就看了,呵呵如图我们可以看到,如果按照中序遍历的方式将得到以上结果,我们现在只观察B节点,从矩形方框我们可以看到,只在这一颗以A为根节点的二叉树中如果要找到B的前驱,而B的前驱又有左子树,我们可以想到遍历方式总是按照左中右的方式进行的,那么在B的前面的(这里指的是便利方式得到的序列中的位置,这点要理解清楚),一定是其左子树中最右的那一颗因为在B的左子树中最后肯定是遍历到最右边的I......依此类推可以想得到前序遍历,后续遍历等。
因此,我们要线索化二叉树,只需考虑到当前的节点和当前节点的前驱节点即可!开始我还呆呆的考虑很多的东西,例如当前节点的左节点,右节点,父节点等等一大串,真是晕头啊!
以下同样列出代码:
结构体代码:
//结构体定义
typedef enum
{
SubTree,
Thread
}NodeFlag;
typedef struct ThreadTree
{
char dat;
NodeFlag lflag;
NodeFlag rflag;
struct ThreadTree *left;
struct ThreadTree *right;
}ThreadBinTree;
//全局变量 保存中序遍历的节点
ThreadBinTree *Previous=NULL;
实现代码:
//中序线索化二叉树
void BinTreeThread_LDR(ThreadBinTree *bt)
{
if(bt)
{
BinTreeThread_LDR(bt->left);
bt->lflag=bt->left ? SubTree : Thread;
bt->rflag=bt->right? SubTree : Thread;
if(Previous)
{
if(Previous->rflag==Thread)
Previous->right=bt;
if(bt->lflag==Thread)
bt->left=Previous;
}
Previous=bt;
BinTreeThread_LDR(bt->right);
}
}
//中序线索二叉树求后继节点
ThreadBinTree *BinTreeNext_LDR(ThreadBinTree *bt)
{
ThreadBinTree *nextnode;
if(!bt)
return NULL;
if(bt->rflag==Thread)
return bt->right;
else
{
nextnode=bt->right;
while(nextnode->lflag==SubTree)
nextnode=nextnode->left;
return nextnode;
}
}
//中序线索二叉树求前驱节点
ThreadBinTree* BinTreePrev_LDR(ThreadBinTree* bt)
{
ThreadBinTree *prevnode;
if(!bt)
return NULL;
if(bt->lflag==Thread)
return bt->left;
else
{
prevnode=bt->left;
while(prevnode->rflag==SubTree)
prevnode=prevnode->right;
return prevnode;
}
}
//遍历线索二叉树
void ThreadBinTree_LDR(ThreadBinTree *bt)
{
if(bt)
{
while(bt->lflag==SubTree)
bt=bt->left;
do
{
printf("%c ",bt->dat);
bt=BinTreeNext_LDR(bt);
}while(bt);
}
}
大家可以下载源代码测试链接在本章的开头部分!