线索二叉树是另一种分步遍历二叉树的方法。它既可以从前向后分步遍历二叉树,又可以从后向前分步遍历二叉树。
当按某种规则遍历二叉树时,保存遍历时得到的结点的后继结点信息和前驱结点信息的最常用的方法是建立线索二叉树。
对二叉链存储结构的二叉树分析可知,在有n个结点的二叉树中必定存在n+1个空链域。
规定:当某结点的左指针为空时,令该指针指向按某种方法遍历二叉树时得到的该结点的前驱结点;当某结点的右指针为空时,令该指针指向按某种方法遍历二叉树时得到的该结点的后继结点。仅仅这样做会使我们不能区分左指针指向的结点到底是左孩子结点还是前驱结点,右指针指向的结点到底是右孩子结点还是后继结点。因此我们再在结点中增加两个线索标志位来区分这两种情况。
线索标志位定义如下:
结点中指向前驱结点和后继结点的指针称为线索。在二叉树的结点上加上线索的二叉树称作线索二叉树。对二叉树以某种方法(如前序、中序或后序方法)遍历使其变为线索二叉树的过程称作按该方法对二叉树进行的线索化。
一旦建立了某种方式的线索二叉树后,用户程序就可以像操作双向链表一样操作该线索二叉树。
例如,一旦建立了中序线索二叉树后,用户程序就可以设计一个正向循环结构遍历该二叉树的所有结点,循环初始定位在中序线索二叉树的第一个结点位置,每次循环使指针指向当前结点的中序遍历的后继结点位置,当指针指向中序线索二叉树的最后一个结点位置后循环过程结束;或者用户程序可以设计一个反向循环结构遍历该二叉树的所有结点,循环初始定位在中序线索二叉树的最后一个结点位置,每次循环使指针指向当前结点的中序遍历的前驱结点位置,当指针指向中序线索二叉树的第一个结点位置前循环过程结束。
这种算法设计要求分别设计三个函数:
First():定位在第一个结点位置;
Next():移动到下一个结点位置;
End():是否已经到最后下一个结点位置;
当然,还需要一个根据二叉树构造线索二叉树的函数
typedef struct tNode{
char data;
int leftThread;
struct tNode *leftChild;
struct tNode *rightChild;
int rightThread;
}ThreadBiNode;
typedef struct
{
ThreadBiNode *root;
ThreadBiNode *current;
int nextComplete;
}ThreadBiTree;
void InThread(ThreadBiNode*current, ThreadBiNode** pre)
{
if (current != NULL)
{
InThread(current->leftChild, pre);
if (current->leftChild == NULL)
{
current->leftThread = 1;
current->leftChild = *pre;
}
else current->leftThread = 0;
if (current->rightChild != NULL)
current->rightThread = 0;
else
current->rightThread = 1;
if ((*pre)->rightChild == NULL)
{
(*pre)->rightThread = 1;
(*pre)->rightChild = current;
}
else
current->rightThread = 0;
*pre = current;
InThread(current->rightChild, pre);
}
}
void CreateInThread(ThreadBiNode** root)
{
ThreadBiNode*t = *root;
ThreadBiNode* current, *pre = *root;
*root = (ThreadBiNode*)malloc(sizeof(BiTreeNode));
if(t==NULL)
{
(*root)->leftThread = 0;
(*root)->rightThread =1;
(*root)->leftChild = *root;
(*root)->rightChild = *root;
}
else
{
current = t;
(*root)->leftChild = t;
(*root)->leftThread = 0;
InThread(current, &pre);
pre->rightChild = *root;
pre->rightThread = 1;
(*root)->rightChild = pre;
(*root)->rightThread = 1;
}
}
ThreadBiNode* GetTreeNode(char item, ThreadBiNode* left, ThreadBiNode* right)
{
ThreadBiNode* p = (ThreadBiNode*)malloc(sizeof(BiTreeNode));
p->data = item;
p->leftChild = left;
p->rightChild = right;
return p;
}
void MakeCharTree(ThreadBiNode**root)
{
ThreadBiNode*b, *c, *d, *e, *f, *g;
g = GetTreeNode('G', NULL, NULL);
d = GetTreeNode('D', NULL, g);
b = GetTreeNode('B', d, NULL);
e = GetTreeNode('E', NULL, NULL);
f= GetTreeNode('F', NULL, NULL);
c = GetTreeNode('C', e, f);
*root = GetTreeNode('A', b, c);
}
void ThreadInitiate(ThreadBiTree *tree, ThreadBiNode *root)
{
tree->root = root;
tree->current = root;
if (root == NULL)
tree->nextComplete = 1;
else
tree->nextComplete = 0;
}
void First(ThreadBiTree *tree)
//定位在中序线索二叉树的第一个结点
{
tree->current = tree->root; //定位根结点
while (tree->current->leftThread == 0) //找到最左子树结点
tree->current = tree->current->leftChild;
if (tree->current == tree->root) tree->nextComplete = 1;
else tree->nextComplete = 0;
}
void Next(ThreadBiTree *tree)
//定位在中序线索二叉树当前结点的下一个结点
{
ThreadBiNode *p = tree->current->rightChild;
if (tree->nextComplete == 1) return;
if (tree->current->rightThread == 0) //若有右孩子结点
while (p->leftThread == 0) p = p->leftChild; //找到最左子树结点
tree->current = p;
if (tree->current == tree->root) tree->nextComplete = 1;
}
int EndOfNext(ThreadBiTree *tree)
//判断是否已到中序线索二叉树的最后一个结点
{
return tree->nextComplete;
}
void main_thread(void)
{
ThreadBiNode *root;
ThreadBiTree tree;
MakeCharTree(&root);
CreateInThread(&root);
printf("二叉树中序正向遍历序列为:");
ThreadInitiate(&tree, root); //循环初始化
for (First(&tree); !EndOfNext(&tree); Next(&tree))
printf("%c ", tree.current->data);
}