线索二叉树简单来说就是利用原来的指针域通过标记区分将每个节点的直接前驱和后继结点记录下来,这个记录下前后结点的过程就叫线索化,线索化后的二叉树就是线索二叉树。
主要写了前序、中序、后序三种方法的线索化,前序和中序的两种遍历方式。
目录
//树结构定义及主函数
typedef char ElemType; //定义元素类型
#define MAXSIZE 10 //定义队列的最大值
typedef struct Hbitree { //定义树的结构体
ElemType data; //值域
struct Hbitree* left; //左儿子
struct Hbitree* right; //右儿子
int ltag; //左标志,为0表示有儿子、没有线索化,为1表示没有儿子、线索化了
int rtag; //右标志,为0表示有儿子、没有线索化,为1表示没有儿子、线索化了
}Hbi, *Hbitree;
//菜单函数
void menu() {
printf("----------------\n");
printf(" 1、前序线索化\n");
printf(" 2、中序线索化\n");
printf(" 3、后序线索化\n");
printf("----------------\n");
}
//主函数
int main(){
while (1) {
int chose = 0;
Hbitree root = NULL;
ElemType ch;
printf("----------------\n");
printf("请先初始化树(用'#'表示虚结点):\n");
root = InitTree(root); //初始化树
menu();
scanf("%d", &chose);
getchar(); //清除缓冲区
switch (chose) {
case 1:
PreThead(root); //前序线索化
printf("前序线索化成功!\n");
printf("当前树的前序遍历为:\n");
PreOrder(root);
printf("\n");
free(root);
break;
case 2:
InThead(root); //中序线索化
printf("中序线索化成功!\n");
printf("当前树的中序遍历为:\n");
InOrder(root);
printf("\n");
free(root);
break;
case 3:
PostThead(root); //后序线索化
printf("后序线索化成功!\n");
printf("当前树的后序遍历为:\n难写没写\n");
break;
default:return;
}
}
return 0;
}
//用队列初始化树
//用队列初始化树
Hbitree InitTree(Hbitree root) {
Hbitree Queue[MAXSIZE+1]; //+1防止溢出
int front = 1; //队首指针,从1开始方便后面的左右儿子判断
int rear = 0; //队尾指针。从0开始方便后面计算
Hbitree new; //新节点
ElemType ch; //结点值
while ((ch = getchar()) != '\n') {
new = (Hbitree)malloc(sizeof(Hbi));
new->data = ch;
new->left = new->right = NULL;
new->ltag = new->rtag = 0;
Queue[++rear] = new; //入队
if (Queue[rear]->data == '#')//虚结点
Queue[rear] = NULL;
if (root == NULL)
root = new;
if (rear == front * 2) {
//判断是否是左儿子
Queue[front]->left = Queue[rear];
}
if (rear == front * 2 + 1) { //判断是否是右儿子
Queue[front]->right = Queue[rear];
front++; //如果有右儿子那就将front加1,出队
}
}
return root;
}
//前序线索化
//PRE为线索时的p的前一个结点
Hbitree PRE = NULL;
//前序线索化
void PreThead(Hbitree p) {
if (p) {
if (p->left == NULL) {//没有左儿子
p->ltag = 1;//线索化
p->left = PRE;//指向前驱结点
}
//因为当前结点和其后继并不好线索化,所以判断前一个结点的后继
if (PRE && (PRE->right == NULL)) {//判断前驱结点有没有右儿子
PRE->rtag = 1;//线索化
PRE->right = p;//前一个结点的后继指向当前
}
PRE = p;//操作完一个结点后就变成了前驱
if (p->ltag == 0)//如果左儿子没有线索化,说明有左儿子,那就进入左儿子
PreThead(p->left);
if (p->rtag == 0)//同上
PreThead(p->right);
}
}
//中序线索化
//PRE为线索时的p的前一个结点
//Hbitree PRE = NULL;
//中序线索化
void InThead(Hbitree p) {
if (p) {
//进入左子树,找到第一个结点
InThead(p->left);
if (p->left == NULL) {
p->ltag = 1;
p->left = PRE;
}
if (PRE && (PRE->right == NULL)) {
PRE->rtag = 1;
PRE->right = p;
}
PRE = p;
InThead(p->right);
}
}
//后序线索化
//PRE为线索时的p的前一个结点
//Hbitree PRE = NULL;
//后序线索化
void PostThead(Hbitree p) {
if (p) {
PostThead(p->left);
PostThead(p->right);
if (p->left == NULL) {
p->ltag = 1;
p->left = PRE;
}
if (PRE && (PRE->right == NULL)) {
PRE->rtag = 1;
PRE->right = p;
}
PRE = p;
}
}
遍历某种次序的线索二叉树时,只需要从该次序下的开始结点出发,一直找它的后继结点,直到后继为空。这对于前序和中序线索化后的二叉树来说很简单,但是对于后序线索二叉树来说十分麻烦,所以只写了前两种遍历方式。
//前序遍历线索二叉树
//查找前序后继
Hbitree PreNext(Hbitree p) {
//没有左孩子时,后继就是右孩子
if (p->ltag == 1)
return p->right;
//有左孩子时,后继就是左孩子
else
return p->left;
}
//前序遍历线索二叉树
void PreOrder(Hbitree root) {
Hbitree p = root;
while (p) {
printf("%c ",p->data);
p = PreNext(p);//找p的后继
}
}
//中序遍历线索二叉树
//查找中序后继
Hbitree InNext(Hbitree root) {
Hbitree p = root;
//如果p的右儿子被线索化了,后继就是线索化的右儿子
if (p->rtag == 1)
return p->right;
//如果p的右儿子没被线索化,后继就是p的右子树的最左的一个结点
else {
p = p->right;//p变成右儿子
while (p && p->ltag == 0) {//p为空就直接返回
//如果有左儿子
p = p->left;
}
}
return p;
}
//中序遍历线索二叉树
void InOrder(Hbitree root) {
Hbitree p = root;
while (p->ltag == 0)//先找开始结点,就是最左的一个结点
p = p->left;
while (p) {
printf("%c ", p->data);
p = InNext(p);//找p的后继
}
}