线索化之前
线索化之后
//LinkBinTreeThread.h
#ifndef _LINKBINTREETHREAD_H_
#define _LINKBINTREETHREAD_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
*存在n个节点的的二叉树拥有 n + 1 个空指针域,也就是叶子节点与度为1的节点上的空指针域
*利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点地址(这种附加的指针称为"线索"),线索完成之后二叉树就相当于是某种遍历次序下的线性结构
*下面所有的方法都依托在中序线索化下进行
*/
typedef char Element;
typedef enum { Link, Thread } Tag; //链线标记,link表示节点的指针域为孩子指针,Thread表示节点的指针域是线索
typedef struct _BinTreeNode
{
Element data;
struct _BinTreeNode *LeftChild; //左孩子指针域,为链时,指向存在的左孩子,否则指向空,为线时,指向存在的前驱,否则指向空
struct _BinTreeNode *RightChild; //右孩子指针域,为链时,指向存在的左孩子,否则指向空,为线时,指向存在的前驱,否则指向空
Tag LeftTag; //左孩子线索标记,为Link,即为链时说明左孩子指针指向的是存在的孩子或空
Tag RightTag; //右孩子线索标记,为Thread,即线时说明右孩子指向存在的后继或空
}BinTreeNode;
typedef BinTreeNode * BinTree;
void InitBinTree(BinTree *tree);
//创建二叉树
void CreateBinTree(BinTree *tree, const char *VLR, const char *LVR, int n);
//中序线索化
void BinTreeThread(BinTree *tree);
//返回树tree中最先访问(中序遍历时)的节点
BinTreeNode *First(BinTree tree);
//最后访问(中序遍历时)
BinTreeNode *Last(BinTree tree);
//后继节点
BinTreeNode *Next(BinTree tree, BinTreeNode *cur);
//前驱节点
BinTreeNode *pre(BinTree tree, BinTreeNode *cur);
//线性方式查找
BinTreeNode *Search(BinTree tree, Element e);
//父节点
BinTreeNode *Parent(BinTree tree, BinTreeNode *cur);
//中序遍历
void InOrder(BinTree tree);
#endif // _LINKBINTREETHREAD_H_
//LinkBinTreeThread.c
#include "LinkBinTreeThread.h"
void InitBinTree(BinTree *tree)
{
*tree = NULL;
}
void CreateBinTree(BinTree *tree, const char *VLR, const char *LVR, int n)
{
if( 0 == n )
{
*tree = NULL;
return ;
}
int i = 0;
while (VLR[0] != LVR[i])
i++;
*tree = (BinTreeNode *)malloc(sizeof(BinTreeNode));
if( NULL == *tree )
return ;
(*tree)->data = VLR[0];
(*tree)->LeftTag = Link;
(*tree)->RightTag = Link;
CreateBinTree(&(*tree)->LeftChild, VLR + 1, LVR, i);
CreateBinTree(&(*tree)->RightChild, VLR + i + 1, LVR + i + 1, n - i - 1);
}
//根中序遍历一样,如果想要线索化根,先要线索化根的左子树。。。。。。。
static void BinTreeThread_inter(BinTree *tree, BinTreeNode **pre)
{
if( NULL == *tree )
return ;
BinTreeThread_inter(&(*tree)->LeftChild, pre); //先线索左子树
if( NULL == (*tree)->LeftChild ) //节点的左孩子指针为空(孩子指针为空说明能被线索)
{
(*tree)->LeftChild = *pre; //使空左孩子指针指向前驱
(*tree)->LeftTag = Thread; //改变标记为线索
}
if( NULL != *pre && NULL == (*pre)->RightChild ) //前驱节点不为空且前驱节点的右孩子指针为空
{
(*pre)->RightChild = *tree; //改变标记为线索
(*pre)->RightTag = Thread; //使空右孩子指针指向后驱
}
*pre = *tree; //使前驱指针指向当前栈帧下的根节点,以便后续栈帧中的根节点线索
BinTreeThread_inter(&(*tree)->RightChild, pre); //最后线索右子树
} //当意图线索整个二叉树最右边的叶子节点时,即*pre指向最右边的叶子节点时,当前栈帧中的*tree指向了叶子节点的空孩子,退出了递归,因此没能对二叉树的最右边的叶子节点线索化
void BinTreeThread(BinTree *tree)
{
BinTreeNode *pre = NULL; //指向前驱节点的指针
BinTreeThread_inter(tree, &pre);
//在此处将二叉树最右边的叶子节点进行线索化
pre->RightTag = Thread;
pre->RightChild = NULL;
}
//中序下最左边的叶子节点为最先访问的节点
BinTreeNode *First(BinTree tree)
{
if( NULL == tree )
return NULL;
BinTreeNode *p = tree;
while (Thread != p->LeftTag) //亦可通过判空。判链来移动
p = p->LeftChild;
return p;
}
//中序下最右边的叶子节点是最后访问的节点
BinTreeNode *Last(BinTree tree)
{
if( NULL == tree )
return NULL;
BinTreeNode *p = tree;
while (Thread != p->RightTag) //亦可通过判空。判链来移动
p = p->RightChild;
return p;
}
BinTreeNode *Next(BinTree tree, BinTreeNode *cur)
{
if( NULL == tree || NULL == cur )
return NULL;
if( Thread == cur->RightTag ) //如果当亲节点右标记为线,则后继节点则是当前节点的右孩子指针所指节点
return cur->RightChild;
return First(cur->RightChild); //不为线则是其右子树中最先被访问的节点,如对于B节点来说,其后继为子右子树D中最先访问的节点,也就是E
}
BinTreeNode *pre(BinTree tree, BinTreeNode *cur)
{
if( NULL == tree || NULL == cur )
return NULL;
if( Thread == cur->LeftTag )
return cur->LeftChild;
return Last(cur->LeftChild);
}
BinTreeNode *Search(BinTree tree, Element e)
{
if( NULL == tree )
return NULL;
BinTreeNode *p = NULL;
for(p = First(tree); NULL != p; p = Next(tree, p))
{
if( p->data == e )
return p;
}
return NULL;
}
BinTreeNode *Parent(BinTree tree, BinTreeNode *cur)
{
if( NULL == tree || NULL == cur )
return NULL;
//当前节点左孩子标记为线,其前驱节点可能为父节点,如果前驱节点的右孩子为当前节点,那么当前节点的前驱父节点,如H、G、F
if( Thread == cur->LeftTag && NULL != cur->LeftChild && cur->LeftChild->RightChild == cur )
return cur->LeftChild;
//如C、E
if( Thread == cur->RightTag && NULL != cur->RightChild && cur->RightChild->LeftChild == cur )
return cur->RightChild;
//左右指针都不是线,看当前节点左子树中最先访问的节点的左孩子指针所指节点的右孩子是不是当前节点,如果是,则最先访问节点的左孩子指针所指节点就是父节点,如D
BinTreeNode *first = First(cur->LeftChild);
if( NULL != first->LeftChild && first->LeftChild->RightChild == cur )
return first->LeftChild;
//父节点是必然存在的,如果都不是上述节点,那么必然是当前节点的右子树中最后访问的节点的右指针所指节点,如B的父节点就是其右子树D中最后访问节点F的右指针所指,如B
return Last(cur->RightChild)->RightChild;
}
void InOrder(BinTree tree)
{
if( NULL == tree )
return ;
BinTreeNode *p = NULL;
for(p = First(tree); p != NULL; p = Next(tree, p))
printf("%c", p->data);
printf("\n");
}
#include <stdio.h>
#include "LinkBinTreeThread.h"
int main(int argc, char *argv[])
{
const char *VLR = "ABCDEFGH";
const char *LVR = "CBEDFAGH";
BinTree tree;
BinTreeNode *p = NULL, *q = NULL;
int len = strlen(VLR);
InitBinTree(&tree);
CreateBinTree(&tree, VLR, LVR, len);
BinTreeThread(&tree);
p = First(tree);
if( NULL != p )
printf("第一个被访问节点的值为%c\n", p->data);
p = Last(tree);
if( NULL != p )
printf("最后一个被访问节点的值为%c\n", p->data);
p = Search(tree, 'H');
if( NULL != p )
printf("查找到的节点的值为%c\n", p->data);
else
printf("未查找到该节点\n");
q = Next(tree, p);
if (NULL != q)
printf("查找到的后续节点的值为%c\n", q->data);
else
printf("未查找到该后继节点\n");
q = pre(tree, p);
if (NULL != q)
printf("查找到的前驱节点的值为 %c\n", q->data);
else
printf("未查找到该前驱节点\n");
q = Parent(tree, p);
if (NULL != q)
printf("查找到的父点的值为%c\n", q->data);
else
printf("未查找到父节点\n");
InOrder(tree);
return 0;
}