概念
对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
注意:线索链表解决了无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题,解决了二叉链表找左、右孩子困难的问题。
实际上也就是当二叉树的左右孩子结点为NUll的时候,指向的地址是浪费的,为了减少浪费我们可以通过将其指向前驱或者后续来利用这些无用的空间,提升查找速度,值得注意的是实际使用中我们要根据选择的二叉树遍历规则来进行对应的指向(前序、中序、后序)要保持一直.一般来说我们使用中序遍历进行二叉树线索化
线索二叉树的存储我们选择线性存储方式,指针指向的关系便于查找,顺序存储的二叉树会非常麻烦,同时也线索二叉树也存在一些不足:
优势
- 利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间。
- 任意一个结点都能直接找到它的前驱和后继结点。
不足
- 结点的插入和删除麻烦,且速度也较慢。
- 线索子树不能共用。
分析:
- 相对于二叉树我们需要添加标示符ltag和rtag,当ltag和rtag为0时,leftChild和rightChild分别是指向左孩子和右孩子的指针;否则,leftChild是指向结点前驱的线索(pre),rightChild是指向结点的后继线索(suc).由于标志只占用一个二进位,每个结点所需要的存储空间节省很多。
// 头文件与宏定义
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Status;
typedef char CElemType;
/* 字符型以空格符为空 */
CElemType Nil='#';
typedef char String[24]; /* 0号单元存放串的长度 */
String str;
//我们可以创建出线索二叉树的数据结构
/* Link==0表示指向左右孩子指针, */
/* Thread==1表示指向前驱或后继的线索 */
typedef enum {
Link, //0
Thread //1
} PointerTag;
/* 线索二叉树存储结点结构*/
typedef struct BiThrNode{
CElemType data; //数据
struct BiThrNode *lchild; //左孩子指针
struct BiThrNode *rchild; //右孩子指针
PointerTag LTag; //左孩子标记 Link 为真孩子, Thread 为前驱
PointerTag RTag; //右孩子标记 Link 为真孩子, Thread 为后继
}BiThrNode,*BiThrTree;
2.创建二叉树
// 创建二叉树
Status createBiTree(BiThrTree *T){
CElemType h;
//获取字符
h = str[indexs++];
if (h == Nil) {
*T = NULL;
}else{
*T = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*T) {
exit(OVERFLOW);
}
//生成根结点(前序)
(*T)->data = h;
//递归构造左子树
CreateBiThrTree(&(*T)->lchild);
//存在左孩子->将标记LTag设置为Link
if ((*T)->lchild) (*T)->LTag = Link;
//递归构造右子树
CreateBiThrTree(&(*T)->rchild);
//存在右孩子->将标记RTag设置为Link
if ((*T)->rchild) (*T)->RTag = Link;
}
return OK;
}
- 中序遍历二叉树T,并将其中序线索化,Thrt指向头结点,创建头结点,头节点的左孩子指向第一个结点,最后一个叶子结点的右孩子指向头节点的右孩子,这样可以很好的通过最后一个结点访问到第一个结点
/* 中序遍历二叉树T,并将其中序线索化,Thrt指向头结点 */
//全局变量始终表示前驱
BiThrTree pre;
Status InOrderThreading(BiThrTree *Thrt , BiThrTree T){
*Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
if (! *Thrt) {
exit(OVERFLOW);
}
//建立头结点;
(*Thrt)->LTag = Link;
(*Thrt)->RTag = Thread;
//右指针回指向
(*Thrt)->rchild = (*Thrt);
/* 若二叉树空,则左指针回指 */
if (!T) {
(*Thrt)->lchild=*Thrt;
}else{
(*Thrt)->lchild=T;
pre=(*Thrt);
//中序遍历进行中序线索化
InThreading(T);
//最后一个结点rchil 孩子
pre->rchild = *Thrt;
//最后一个结点线索化
pre->RTag = Thread;
(*Thrt)->rchild = pre;
}
return OK;
}
3.中序遍历二叉线索树
/*中序遍历二叉线索树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;
while(p->RTag==Thread&&p->rchild!=T)
{
p=p->rchild;
visit(p->data); /* 访问后继结点 */
}
p=p->rchild;
}
return OK;
}
Status StrAssign(String T,char *chars)
{
int i;
if(strlen(chars)>MAXSIZE)
return ERROR;
else
{
T[0]=strlen(chars);
for(i=1;i<=T[0];i++)
T[i]=*(chars+i-1);
return OK;
}
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, 线索化二叉树!\n");
BiThrTree H,T;
//StrAssign(str,"ABDH#K###E##CFI###G#J##");
StrAssign(str,"ABDH##I##EJ###CF##G##");
CreateBiThrTree(&T); /* 按前序产生二叉树 */
InOrderThreading(&H,T); /* 中序遍历,并中序线索化二叉树 */
InOrderTraverse_Thr(H);
printf("\n\n");
return 0;
}