二叉线索树
概念 普通二叉树只能找到结点的左右孩子信息,而该结点的直接前驱和直接后继只能在遍历过程中获得。 若可将遍历后对应的有关前驱和后继预存起来,则从第一个结点开始就能很快“顺藤摸瓜”而遍历整个树了。 |
理解:
线索化核心算法的理解类似使用2个辅助指针变量交替的挖字符串
代码讲解
按照严老师 增加一个头结点
#define _CRT_SECURE_NO_WARNINGS
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
typedef int Status;
typedef char TElemType;
typedef enum{
Link, //Link = 0 表示指向左右孩子指针
Thread //Thread = 1 表示指向前驱或后继的线索
}PointerTag;
//二叉线索存储节点
typedef struct BiThrNode{
TElemType data; //节点数据
struct BiThrNode *lchild, *rchild; //左右孩子指针
PointerTag LTag; //左标志
PointerTag RTag; //右标志
}BiThrNode, *BiThrTree;
TElemType Nil = '#';//字符型以空格符为空
Status visit(TElemType e)
{
printf("%c", e);
return OK;
}
//按前序输入二叉线索树中结点的值,构造二叉线索树
/* 0(整型)/空格(字符型)表示空结点 */
//以# 法 按照给定的先序序列 建立二叉链表
Status CreateBiThrTree(BiThrTree *T)//二级指针
{
TElemType h;
scanf("%c", &h);
if (h == Nil){
*T = NULL;
}
else{
*T =(BiThrTree)malloc(sizeof(BiThrNode));
if (NULL == *T){
exit(OVERFLOW);
}
(*T)->data = h;//生产根节点
CreateBiThrTree(&(*T)->lchild);//递归构造左子树
if ((*T)->lchild){//有左孩子
(*T)->LTag = Link;
}
CreateBiThrTree(&(*T)->rchild);//递归构造右子树
if ((*T)->rchild){//有右孩子
(*T)->RTag = Link;
}
}
return OK;
}
BiThrTree pre;//全局变量,始终指向刚刚访问过得节点
//中序遍历进行中序线索化 算法
void InThreading(BiThrTree p)
{
if (p){
InThreading(p->lchild); //递归左子树线索化
if (p->lchild == NULL){ //没有左孩子
p->LTag = Thread; //前驱线索
p->lchild = pre; //左孩子指向前驱指针
}
if (pre->rchild == NULL){ //前驱没有右孩子
pre->RTag = Thread; //后继线索
pre->rchild = p; //前驱右孩子指向后继
}
pre = p; //保持pre指向p的前驱,辅助指针交替移动
InThreading(p->rchild); //递归右子树线索化
}
}
//中序遍历二叉树T,并将其中序线索化,Thrt指向头结点
Status InOrderThreading(BiThrTree *Thrt, BiThrTree T)
{
//按照严老师的设计将二叉线索链表变为一个环
*Thrt = (BiThrTree)malloc(sizeof(BiThrNode));
if (NULL == *Thrt){
exit(OVERFLOW);
}
(*Thrt)->LTag = Link; //建立头结点
(*Thrt)->RTag = Thread;
(*Thrt)->rchild = (*Thrt); //右指针回指
if (!T){
(*Thrt)->lchild = *Thrt; //若二叉树为空,则左指针回指
}
else{
(*Thrt)->lchild = T; //让头结点的左孩子指向树根,避免悬空指针
pre = (*Thrt); //初始化 pre(全局变量)
//------------------------------------------------
InThreading(T); //中序遍历进行中序线索化
//--------------------------------------------------
//线索化完后pre指向了中序遍历的末尾结点
pre->rchild = *Thrt; //让末尾结点的右孩子指向头结点
pre->RTag = Thread; //最后一个节点进行线索化
(*Thrt)->rchild = pre; //头结点的右孩子指向 中序遍历末尾结点,避免悬空指针
}
return OK;
}
//中序遍历 二叉线索树T (头结点)的非递归算法
//线索化的第二种遍历方法: 中序遍历过程中,将每个节点存放在链表中,则可通过链表访问整个树
Status InOrderTraverse_Thr(BiThrTree T)
{
BiThrTree p;
p = T->lchild;//p指向根节点
while (p != T){//访问链表 空树或遍历结束时, p ==T
//------------------------将虚线部分之间内容屏蔽,则就是遍历一个链表
//一直往左走,找到中序遍历的起点
while (p->LTag == Link){
p = p->lchild;
}
//访问左子树为空的节点,打印该节点
if (!visit(p->data)){ //若起点值为空则出错告警
return ERROR;
}
//如果这个结点有右子树且是线索化 ,找到p,然后返回到下一循环125行,一直往左走,类似递归
//若有后继标志,则直接提取p->rchild中线索并访问后继结点;
while (p->RTag == Thread && p->rchild != T){
p = p->rchild;
visit(p->data);//访问后继节点
}
//------------------------
p = p->rchild; //当前结点右域不空或已经找好了后继,则一律从结点的右子树开始重复{ }的全部过程
}
return OK;
}
int main()
{
BiThrTree H, T;
printf("请按前序输入二叉树(如:'ABDH##I##EJ###CF##G##')\n");
CreateBiThrTree(&T);//按照前序 #法创建二叉树
InOrderThreading(&H, T);//中序遍历,并中序线索化二叉树
printf("中序遍历(输出)二叉线索树:\n");
InOrderTraverse_Thr(H);//采用非递归算法中序遍历(输出)二叉线索树
printf("\n");
system("pause");;
return 0;
}