二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在结点结构中增加向前和向后的指针,这种方法增加了存储开销,不可取;二是利用二叉树的空链指针。
1.具体做法
图示:
代码:
typedef char ElemType;
#define END '#'
typedef enum{LINK = 0,THREAD = 1} PointerTag;
typedef struct BiThrNode
{
BiThrNode *leftchild;
BiThrNode *rightchild;
PointerTag Ltag,Rtag; //左右标记
ElemType data;
}BiThrNode,*BinaryThreadTree;
void MakeThread(BiThrNode *p,BiThrNode *&ptr) //线索化
{
if(p != NULL)
{
MakeThread(p->leftchild,ptr);
if(p->leftchild == NULL)
{
p->leftchild = ptr;
p->Ltag = THREAD;
}
if(ptr != NULL && ptr->rightchild == NULL)
{
ptr->rightchild = p;
ptr->Rtag = THREAD;
}
ptr = p;
MakeThread(p->rightchild,ptr);
}
}
void MakeThreadTree(BiThrNode *p) //线索化二叉树
{
BiThrNode *ptr = NULL;
MakeThread(p,ptr);
ptr->Rtag = THREAD; //线索化最后一个结点 ,注意形参的类型
}
代码解释;
2.优势
(1)利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间。
(2)任意一个结点都能直接找到它的前驱和后继结点
不足
(1)结点的插入和删除麻烦,且速度也较慢。
(2)线索子树不能共用。
3.注意事项
- 注意MakeThread函数的第二个参数,他是一个指针的引用。这样做的目的是为了让指针ptr在递归的过程中所发生的改变,能够影响到每一层栈帧。
- 要深刻理解递归调用代码运行轨迹,只有这样才能为用好递归提供先决条件
- 线索化的过程是对空指针的利用,千万不要改变原来指针的指向
- 看代码要深刻理解代码的思路,把我代码的运行轨迹,不要只看效果,那样只会增加莫名的感觉,没实际效果
- 看不懂,想不通,画图、画图、画图,重要的事情说三遍。