【二叉树】
一棵有9个节点且深度为3的二元树，其根节点的值为2，它既不平衡亦未经过排序
一棵简单的满二叉树
在电脑科学中，二元树（Binary tree）是每个节点最多只有两个分支（即不存在分支度大于2的节点）的树结构。通常分支被称作“左子树”或“右子树”。二元树的分支具有左右次序，不能随意颠倒。
二元树的第层至多拥有个节点；深度为的二元树至多总共有个节点（定义根节点所在深度 ），而总计拥有节点数符合的，称为「满二元树」；深度为有个节点的二元树，当且仅当其中的每一节点，都可以和深度的满二元树，序号1到的节点一对一对应时，称为完全二元树。对任何一棵非空的二元树，如果其叶片（终端节点）数为，分支度为2的节点数为，则。
与普通树不同，普通树的节点个数至少为1，而二元树的节点个数可以为0；普通树节点的最大分支度没有限制，而二元树节点的最大分支度为2；普通树的节点无左、右次序之分，而二元树的节点有左、右次序之分。
二元树通常作为资料结构应用，典型用法是对节点定义一个标记函数，将一些值与每个节点相关联。这样标记的二元树就可以实现二元搜寻树和二元堆积，并应用于高效率的搜索和排序。
== 图论中的定义 ==
二元树是一个连通的无环图，并且每一个顶点的度不大于3。有根二元树还要满足根节点的度不大于2。有了根节点之后，每个顶点定义了唯一的父节点，和最多2个子节点。然而，没有足够的资讯来区分左节点和右节点。如果不考虑连通性，允许图中有多个连通分量，这样的结构叫做森林。
== 性质 ==
二元树是一个有根树，并且每个节点最多有2个子节点。非空的二叉树，若树叶总数为 n0，分支度为2的总数为 n2，则 n0 = n2 + 1。
300px
== 特殊类型 ==
400px
=== 完美二叉树 ===
一棵深度为k，且有个节点的二元树，称为完美二元树（Perfect Binary Tree）。这种树的特点是每一层上的节点数都是最大节点数。
==== 性质 ====
对于一棵深度为  的完美二叉树：
* 共有  个结点
* 结点个数一定为奇数
* 第  层有  个结点
* 有  个叶子
=== 完全二叉树  ===
在一颗二元树中，若除最后一层外的其余层都是满的，并且最后一层要么是满的，要么在右边缺少连续若干节点，则此二元树为完全二元树（Complete Binary Tree）。具有n个节点的完全二元树的深度为。深度为k的完全二元树，至少有个节点，至多有个节点。
== 存储 ==
在程式设计语言中能用多种方法来构造二元树。
=== 顺序存储表示 ===
一个存储在阵列中的完全二元树
二元树可以用阵列或链表来存储，若是满二元树就能紧凑排列而不浪费空间。如果某个节点的索引为i，（假设根节点的索引为0）则在它左子节点的索引会是，以及右子节点会是；而它的父节点（如果有）索引则为。这种方法更有利于紧凑存储和更好的访问的局部性，特别是在前序遍历中。然而，它需要连续的存储空间，这样在存储高度为h的n个节点所组成的一般树时，将浪费很多空间。在最糟糕的情况下，如果深度为h的二元树其每个节点都只有右孩子，则该存储结构需要占用的空间，实际上却有h个节点，浪费了不少空间，是顺序存储结构的一大缺点。
==== 存储结构 ====
/* 二叉树的顺序存储表示 */
#define MAX_TREE_SIZE 100 /* 二叉树的最大节点数 */
typedef TElemType SqBiTreeMAX_TREE_SIZE; /* 0号单元存储根节点 */
typedef struct
{
int level,order; /* 即节点的层(按满二叉树计算) */
}position;
==== 基本操作 ====
/* 二元树的顺序存储的基本操作(23个)*/
#define ClearBiTree InitBiTree /* 在顺序存储结构中，两函数完全一样 */
#define DestroyBiTree InitBiTree /* 在顺序存储结构中，两函数完全一样 */
void InitBiTree(SqBiTree T) －－－（SqBiTree & T）
{ /* 构造空二元树T。因为T是阵列名称，故不需要& */
int i;
for(i=0;i
void CreateBiTree(SqBiTree T)
{ /* 按层序次序输入二叉树中结点的值(字元型或整型), 构造顺序存储的二叉树T */
int i=0;
#if CHAR /* 结点类型为字元 */
int l;
char sMAX_TREE_SIZE;
InitBiTree(T); /* 构造空二元树T */
printf("请按层序输入结点的值(字元)，空格表示空结点，节点数≦%d:\n",MAX_TREE_SIZE);
gets(s); /* 输入字串 */
l=strlen(s); /* 求字串的长度 */
for(;i
int BiTreeDepth(SqBiTree T)
{ /* 初始条件：二元树T存在。操作结果：返回T的深度 */
int i,j=-1;
for(i=MAX_TREE_SIZE-1;i>=0;i--) /* 找到最后一个节点 */
if(Ti!=Nil)
break;
i++; /* 为了便于计算 */
do
j++;
while(i>=pow(2,j));   /*pow是原型为double pow( double x, double y ),计算x的y次方,h = log2k + 1来计算二元树的深度*/
return j;
}
Status Root(SqBiTree T,TElemType *e)
{ /* 初始条件：二元树T存在。操作结果：当T不空，用e返回T的根，返回OK；否则返回ERROR，e无定义 */
if(BiTreeEmpty(T)) /* T空 */
return ERROR;
else
{
*e=T0;
return OK;
}
}
TElemType Value(SqBiTree T,position e)
{ /* 初始条件：二元树T存在，e是T中某个结点(的位置) */
/* 操作结果：返回处于位置e(层,本层序号)的结点的值 */
return T(int)pow(2,e.level-1)+e.order-2;
}
Status Assign(SqBiTree T,position e,TElemType value)
{ /* 初始条件：二叉树T存在，e是T中某个结点(的位置) */
/* 操作结果：给处于位置e(层,本层序号)的结点赋新值value */
int i=(int)pow(2,e.level-1)+e.order-2; /* 将层、本层序号转为矩阵的序号 */
if(value!=Nil&&T(i+1)/2-1==Nil) /* 给叶子赋非空值但双亲为空 */
return ERROR;
else if(value==Nil&&(Ti*2+1!=Nil
Ti*2+2!=Nil)) /*  给双亲赋空值但有叶子(不空) */
return ERROR;
Ti=value;
return OK;
}
TElemType Parent(SqBiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：若e是T的非根结点，则返回它的双亲，否则返回＂空＂ */
int i;
if(T0==Nil) /* 空树 */
return Nil;
for(i=1;i
TElemType LeftChild(SqBiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点。操作结果：返回e的左孩子。若e无左孩子,则返回"空" */
int i;
if(T0==Nil) /* 空树 */
return Nil;
for(i=0;i
TElemType RightChild(SqBiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点。操作结果：返回e的右孩子。若e无右孩子,则返回"空" */
int i;
if(T0==Nil) /* 空树 */
return Nil;
for(i=0;i
TElemType LeftSibling(SqBiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：返回e的左兄弟。若e是T的左孩子或无左兄弟，则返回＂空＂ */
int i;
if(T0==Nil) /* 空树 */
return Nil;
for(i=1;i
TElemType RightSibling(SqBiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：返回e的右兄弟。若e是T的右孩子或无右兄弟，则返回＂空＂ */
int i;
if(T0==Nil) /* 空树 */
return Nil;
for(i=1;i
void Move(SqBiTree q,int j,SqBiTree T,int i) /* InsertChild()用到。加 */
{ /* 把从q的j结点开始的子树移为从T的i结点开始的子树 */
if(q2*j+1!=Nil) /* q的左子树不空 */
Move(q,(2*j+1),T,(2*i+1)); /* 把q的j结点的左子树移为T的i结点的左子树 */
if(q2*j+2!=Nil) /* q的右子树不空 */
Move(q,(2*j+2),T,(2*i+2)); /* 把q的j结点的右子树移为T的i结点的右子树 */
Ti=qj; /* 把q的j结点移为T的i结点 */
qj=Nil; /* 把q的j结点置空 */
}
void InsertChild(SqBiTree T,TElemType p,int LR,SqBiTree c)
{ /* 初始条件：二叉树T存在，p是T中某个结点的值，LR为0或1，非空二叉树c与T不相交且右子树为空 */
/* 操作结果: 根据LR为0或1,插入c为T中p结点的左或右子树。p结点的原有左或右子树则成为c的右子树 */
int j,k,i=0;
for(j=0;j
typedef int QElemType; /* 设伫列元素类型为整型(序号) */
#include "c3-2.h" /* 链伫列 */
#include "bo3-2.c" /* 链伫列的基本操作 */
Status DeleteChild(SqBiTree T,position p,int LR)
{ /* 初始条件：二叉树T存在，p指向T中某个结点，LR为1或0 */
/* 操作结果：根据LR为1或0，删除T中p所指结点的左或右子树 */
int i;
Status k=OK; /* 伫列不空的标志 */
LinkQueue q;
InitQueue(&q); /* 初始化伫列，用于存放待删除的结点 */
i=(int)pow(2,p.level-1)+p.order-2; /* 将层、本层序号转为矩阵的序号 */
if(Ti==Nil) /* 此结点空 */
return ERROR;
i=i*2+1+LR; /* 待删除子树的根结点在矩阵中的序号 */
while(k)
{
if(T2*i+1!=Nil) /* 左结点不空 */
EnQueue(&q,2*i+1); /* 入队左结点的序号 */
if(T2*i+2!=Nil) /* 右结点不空 */
EnQueue(&q,2*i+2); /* 入队右结点的序号 */
Ti=Nil; /* 删除此结点 */
k=DeQueue(&q,&i); /* 伫列不空 */
}
return OK;
}
void(*VisitFunc)(TElemType); /* 函数变数 */
void PreTraverse(SqBiTree T,int e)
{ /* PreOrderTraverse()调用 */
VisitFunc(Te);
if(T2*e+1!=Nil) /* 左子树不空 */
PreTraverse(T,2*e+1);
if(T2*e+2!=Nil) /* 右子树不空 */
PreTraverse(T,2*e+2);
}
void PreOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
{ /* 初始条件：二叉树存在，Visit是对结点操作的应用函数 */
/* 操作结果：先序遍历T，对每个结点调用函数Visit一次且仅一次 */
VisitFunc=Visit;
if(!BiTreeEmpty(T)) /* 树不空 */
PreTraverse(T,0);
printf("\n");
}
void InTraverse(SqBiTree T,int e)
{ /* InOrderTraverse()调用 */
if(T2*e+1!=Nil) /* 左子树不空 */
InTraverse(T,2*e+1);
VisitFunc(Te);
if(T2*e+2!=Nil) /* 右子树不空 */
InTraverse(T,2*e+2);
}
void InOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
{ /* 初始条件：二叉树存在，Visit是对结点操作的应用函数 */
/* 操作结果：中序遍历T，对每个结点调用函数Visit一次且仅一次 */
VisitFunc=Visit;
if(!BiTreeEmpty(T)) /* 树不空 */
InTraverse(T,0);
printf("\n");
}
void PostTraverse(SqBiTree T,int e)
{ /* PostOrderTraverse()调用 */
if(T2*e+1!=Nil) /* 左子树不空 */
PostTraverse(T,2*e+1);
if(T2*e+2!=Nil) /* 右子树不空 */
PostTraverse(T,2*e+2);
VisitFunc(Te);
}
void PostOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
{ /* 初始条件：二叉树T存在，Visit是对结点操作的应用函数 */
/* 操作结果：后序遍历T，对每个结点调用函数Visit一次且仅一次 */
VisitFunc=Visit;
if(!BiTreeEmpty(T)) /* 树不空 */
PostTraverse(T,0);
printf("\n");
}
void LevelOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
{ /* 层序遍历二叉树 */
int i=MAX_TREE_SIZE-1,j;
while(Ti==Nil)
i--; /* 找到最后一个非空结点的序号 */
for(j=0;j
void Print(SqBiTree T)
{ /* 逐层、按本层序号输出二叉树 */
int j,k;
position p;
TElemType e;
for(j=1;j
=== 二叉链表存储表示 ===
402x402px
在使用记录或记忆体位址指标的程式设计语言中，二叉树通常用树结点结构来存储。有时也包含指向唯一的父节点的指标。如果一个结点的子结点个数小于2，一些子结点指针可能为空值，或者为特殊的哨兵结点。
使用链表能避免顺序储存浪费空间的问题，演算法和结构相对简单，但使用二叉链表，由于缺乏父链的指引，在找回父节点时需要重新扫描树得知父节点的节点位址。
==== 存储结构 ====
/* 二叉树的二叉链表存储表示 */
typedef struct BiTNode
{
TElemType data;
struct BiTNode *lchild,*rchild; /* 左右孩子指针 */
}BiTNode,*BiTree;
==== 基本操作 ====
/* 二叉树的二叉链表存储的基本操作(22个) */
#define ClearBiTree DestroyBiTree /* 清空二叉树和销毁二叉树的操作一样 */
#include"func6-3.c"
/* 包括InitBiTree()、DestroyBiTree()、PreOrderTraverse()和InOrderTraverse()4函数 */
void CreateBiTree(BiTree *T)
{ /* 演算法6.4：按先序次序输入二叉树中结点的值(可为字元型或整型，在主程中定义)，*/
/* 构造二叉链表表示的二叉树T。变数Nil表示空(子)树。有改动 */
TElemType ch;
scanf(form,&ch);
if(ch==Nil) /* 空 */
*T=NULL;
else
{
*T=(BiTree)malloc(sizeof(BiTNode)); /* 生成根结点 */
if(!*T)
exit(OVERFLOW);
(*T)->data=ch;
CreateBiTree(&(*T)->lchild); /* 构造左子树 */
CreateBiTree(&(*T)->rchild); /* 构造右子树 */
}
}
Status BiTreeEmpty(BiTree T)
{ /* 初始条件：二叉树T存在。操作结果：若T为空二叉树，则返回TRUE，否则FALSE */
if(T)
return FALSE;
else
return TRUE;
}
int BiTreeDepth(BiTree T)
{ /* 初始条件：二叉树T存在。操作结果：返回T的深度 */
int i,j;
if(T==NULL)  /*如果T=NULL,这样写便于理解，当然也可以写成if（!T）*/;
return 0; /* 空树深度为0 */
if(T->lchild)
i=BiTreeDepth(T->lchild); /* i为左子树的深度 */
else
i=0;
if(T->rchild)
j=BiTreeDepth(T->rchild); /* j为右子树的深度 */
else
j=0;
return i>j?i+1:j+1; /* T的深度为其左右子树的深度中的大者+1 */
}
TElemType Root(BiTree T)
{ /* 初始条件：二叉树T存在。操作结果：返回T的根 */
if(BiTreeEmpty(T))
return Nil;
else
return T->data;
}
TElemType Value(BiTree p)
{ /* 初始条件：二叉树T存在，p指向T中某个结点。操作结果：返回p所指结点的值 */
return p->data;
}
void Assign(BiTree p,TElemType value)
{ /* 给p所指结点赋值为value */
p->data=value;
}
typedef BiTree QElemType; /* 设伫列元素为二叉树的指针类型 */
#include"c3-2.h" /* 链伫列 */
#include"bo3-2.c" /* 链伫列的基本操作 */
TElemType Parent(BiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：若e是T的非根结点，则返回它的双亲，否则返回＂空＂*/
LinkQueue q;
QElemType a;
if(T) /* 非空树 */
{
InitQueue(&q); /* 初始化伫列 */
EnQueue(&q,T); /* 树根指针入队 */
while(!QueueEmpty(q)) /* 队不空 */
{
DeQueue(&q,&a); /* 出队，伫列元素赋给a */
if(a->lchild&&a->lchild->data==e
a->rchild&&a->rchild->data==e)
/* 找到e(是其左或右孩子) */
return a->data; /* 返回e的双亲的值 */
else /* 没找到e，则入队其左右孩子指针(如果非空) */
{
if(a->lchild)
EnQueue(&q,a->lchild);
if(a->rchild)
EnQueue(&q,a->rchild);
}
}
}
return Nil; /* 树空或没找到e */
}
BiTree Point(BiTree T,TElemType s)
{ /* 返回二叉树T中指向元素值为s的结点的指标。另加 */
LinkQueue q;
QElemType a;
if(T) /* 非空树 */
{
InitQueue(&q); /* 初始化伫列 */
EnQueue(&q,T); /* 根指针入队 */
while(!QueueEmpty(q)) /* 队不空 */
{
DeQueue(&q,&a); /* 出队，伫列元素赋给a */
if(a->data==s)
return a;
if(a->lchild) /* 有左孩子 */
EnQueue(&q,a->lchild); /* 入队左孩子 */
if(a->rchild) /* 有右孩子 */
EnQueue(&q,a->rchild); /* 入队右孩子 */
}
}
return NULL;
}
TElemType LeftChild(BiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点。操作结果：返回e的左孩子。若e无左孩子,则返回"空" */
BiTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a->lchild) /* T中存在结点e且e存在左孩子 */
return a->lchild->data; /* 返回e的左孩子的值 */
}
return Nil; /* 其余情况返回空 */
}
TElemType RightChild(BiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点。操作结果：返回e的右孩子。若e无右孩子,则返回"空" */
BiTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a->rchild) /* T中存在结点e且e存在右孩子 */
return a->rchild->data; /* 返回e的右孩子的值 */
}
return Nil; /* 其余情况返回空 */
}
TElemType LeftSibling(BiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：返回e的左兄弟。若e是T的左孩子或无左兄弟，则返回＂空＂*/
TElemType a;
BiTree p;
if(T) /* 非空树 */
{
a=Parent(T,e); /* a为e的双亲 */
if(a!=Nil) /* 找到e的双亲 */
{
p=Point(T,a); /* p为指向结点a的指标 */
if(p->lchild&&p->rchild&&p->rchild->data==e) /* p存在左右孩子且右孩子是e */
return p->lchild->data; /* 返回p的左孩子(e的左兄弟) */
}
}
return Nil; /* 其余情况返回空 */
}
TElemType RightSibling(BiTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：返回e的右兄弟。若e是T的右孩子或无右兄弟，则返回＂空＂*/
TElemType a;
BiTree p;
if(T) /* 非空树 */
{
a=Parent(T,e); /* a为e的双亲 */
if(a!=Nil) /* 找到e的双亲 */
{
p=Point(T,a); /* p为指向结点a的指标 */
if(p->lchild&&p->rchild&&p->lchild->data==e) /* p存在左右孩子且左孩子是e */
return p->rchild->data; /* 返回p的右孩子(e的右兄弟) */
}
}
return Nil; /* 其余情况返回空 */
}
Status InsertChild(BiTree p,int LR,BiTree c) /* 形参T无用 */
{ /* 初始条件：二叉树T存在，p指向T中某个结点，LR为0或1，非空二叉树c与T不相交且右子树为空 */
/* 操作结果：根据LR为0或1，插入c为T中p所指结点的左或右子树。p所指结点的 */
/*           原有左或右子树则成为c的右子树 */
if(p) /* p不空 */
{
if(LR==0)
{
c->rchild=p->lchild;
p->lchild=c;
}
else /* LR==1 */
{
c->rchild=p->rchild;
p->rchild=c;
}
return OK;
}
return ERROR; /* p空 */
}
Status DeleteChild(BiTree p,int LR) /* 形参T无用 */
{ /* 初始条件：二叉树T存在，p指向T中某个结点，LR为0或1 */
/* 操作结果：根据LR为0或1，删除T中p所指结点的左或右子树 */
if(p) /* p不空 */
{
if(LR==0) /* 删除左子树 */
ClearBiTree(&p->lchild);
else /* 删除右子树 */
ClearBiTree(&p->rchild);
return OK;
}
return ERROR; /* p空 */
}
typedef BiTree SElemType; /* 设栈元素为二叉树的指针类型 */
#include"c3-1.h" /* 顺序栈 */
#include"bo3-1.c" /* 顺序栈的基本操作 */
void InOrderTraverse1(BiTree T,void(*Visit)(TElemType))
{ /* 采用二叉链表存储结构，Visit是对资料元素操作的应用函数。演算法6.3，有改动 */
/* 中序遍历二叉树T的非递回演算法(利用栈)，对每个资料元素调用函数Visit */
SqStack S;
InitStack(&S);
while(T
StackEmpty(S))
{
if(T)
{ /* 根指针进栈，遍历左子树 */
Push(&S,T);
T=T->lchild;
}
else
{ /* 根指针退栈，访问根结点，遍历右子树 */
Pop(&S,&T);
Visit(T->data);
T=T->rchild;
}
}
printf("\n");
}
void InOrderTraverse2(BiTree T,void(*Visit)(TElemType))
{ /* 采用二叉链表存储结构，Visit是对资料元素操作的应用函数。演算法6.2，有改动 */
/* 中序遍历二叉树T的非递回演算法(利用栈)，对每个资料元素调用函数Visit */
SqStack S;
BiTree p;
InitStack(&S);
Push(&S,T); /* 根指针进栈 */
while(!StackEmpty(S))
{
while(GetTop(S,&p)&&p)
Push(&S,p->lchild); /* 向左走到尽头 */
Pop(&S,&p); /* 空指针退栈 */
if(!StackEmpty(S))
{ /* 访问结点，向右一步 */
Pop(&S,&p);
Visit(p->data);
Push(&S,p->rchild);
}
}
printf("\n");
}
void PostOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ /* 初始条件：二叉树T存在，Visit是对结点操作的应用函数 */
/* 操作结果：后序递回遍历T，对每个结点调用函数Visit一次且仅一次 */
if(T) /* T不空 */
{
PostOrderTraverse(T->lchild,Visit); /* 先后序遍历左子树 */
PostOrderTraverse(T->rchild,Visit); /* 再后序遍历右子树 */
Visit(T->data); /* 最后访问根结点 */
}
}
void LevelOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ /* 初始条件：二叉树T存在，Visit是对结点操作的应用函数 */
/* 操作结果：层序递回遍历T(利用伫列)，对每个结点调用函数Visit一次且仅一次 */
LinkQueue q;
QElemType a;
if(T)
{
InitQueue(&q); /* 初始化伫列q */
EnQueue(&q,T); /* 根指针入队 */
while(!QueueEmpty(q)) /* 伫列不空 */
{
DeQueue(&q,&a); /* 出队元素(指标),赋给a */
Visit(a->data); /* 访问a所指结点 */
if(a->lchild!=NULL) /* a有左孩子 */
EnQueue(&q,a->lchild); /* 入队a的左孩子 */
if(a->rchild!=NULL) /* a有右孩子 */
EnQueue(&q,a->rchild); /* 入队a的右孩子 */
}
printf("\n");
}
}
=== 三叉链表存储表示 ===
基于三叉链表的二叉树的逻辑结构
改进于二叉链表，增加父节点的指引，能更好地实现节点间的访问，不过演算法相对复杂。
当二叉树用三叉链表表示时，有N个结点，就会有N+2个空指针。
==== 存储结构 ====
/* 二叉树的三叉链表存储表示 */
typedef struct BiTPNode
{
TElemType data;
struct BiTPNode *parent,*lchild,*rchild; /* 父、左右孩子指针 */
}BiTPNode,*BiPTree;
==== 基本操作 ====
/* 二叉树的三叉链表存储的基本操作(21个) */
#define ClearBiTree DestroyBiTree /* 清空二叉树和销毁二叉树的操作一样 */
void InitBiTree(BiPTree *T)
{ /* 操作结果：构造空二叉树T */
*T=NULL;
}
void DestroyBiTree(BiPTree *T)
{ /* 初始条件：二叉树T存在。操作结果：销毁二叉树T */
if(*T) /* 非空树 */
{
if((*T)->lchild) /* 有左孩子 */
DestroyBiTree(&(*T)->lchild); /* 销毁左孩子子树 */
if((*T)->rchild) /* 有右孩子 */
DestroyBiTree(&(*T)->rchild); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
*T=NULL; /* 空指针赋0 */
}
}
void CreateBiTree(BiPTree *T)
{ /* 按先序次序输入二叉树中结点的值(可为字元型或整型，在主程中定义)，*/
/* 构造三叉链表表示的二叉树T */
TElemType ch;
scanf(form,&ch);
if(ch==Nil) /* 空 */
*T=NULL;
else
{
*T=(BiPTree)malloc(sizeof(BiTPNode)); /* 动态生成根结点 */
if(!*T)
exit(OVERFLOW);
(*T)->data=ch; /* 给根结点赋值 */
(*T)->parent=NULL; /* 根结点无双亲 */
CreateBiTree(&(*T)->lchild); /* 构造左子树 */
if((*T)->lchild) /* 有左孩子 */
(*T)->lchild->parent=*T; /* 给左孩子的双亲域赋值 */
CreateBiTree(&(*T)->rchild); /* 构造右子树 */
if((*T)->rchild) /* 有右孩子 */
(*T)->rchild->parent=*T; /* 给右孩子的双亲域赋值 */
}
}
Status BiTreeEmpty(BiPTree T)
{ /* 初始条件：二叉树T存在。操作结果：若T为空二叉树，则返回TRUE，否则FALSE */
if(T)
return FALSE;
else
return TRUE;
}
int BiTreeDepth(BiPTree T)
{ /* 初始条件：二叉树T存在。操作结果：返回T的深度 */
int i,j;
if(!T)
return 0; /* 空树深度为0 */
if(T->lchild)
i=BiTreeDepth(T->lchild); /* i为左子树的深度 */
else
i=0;
if(T->rchild)
j=BiTreeDepth(T->rchild); /* j为右子树的深度 */
else
j=0;
return i>j?i+1:j+1; /* T的深度为其左右子树的深度中的大者+1 */
}
TElemType Root(BiPTree T)
{ /* 初始条件：二叉树T存在。操作结果：返回T的根 */
if(T)
return T->data;
else
return Nil;
}
TElemType Value(BiPTree p)
{ /* 初始条件：二叉树T存在，p指向T中某个结点。操作结果：返回p所指结点的值 */
return p->data;
}
void Assign(BiPTree p,TElemType value)
{ /* 给p所指结点赋值为value */
p->data=value;
}
typedef BiPTree QElemType; /* 设伫列元素为二叉树的指针类型 */
#include"c3-2.h" /* 链伫列 */
#include"bo3-2.c" /* 链伫列的基本操作 */
BiPTree Point(BiPTree T,TElemType e)
{ /* 返回二叉树T中指向元素值为e的结点的指标。加 */
LinkQueue q;
QElemType a;
if(T) /* 非空树 */
{
InitQueue(&q); /* 初始化伫列 */
EnQueue(&q,T); /* 根结点入队 */
while(!QueueEmpty(q)) /* 队不空 */
{
DeQueue(&q,&a); /* 出队，伫列元素赋给a */
if(a->data==e)
return a;
if(a->lchild) /* 有左孩子 */
EnQueue(&q,a->lchild); /* 入队左孩子 */
if(a->rchild) /* 有右孩子 */
EnQueue(&q,a->rchild); /* 入队右孩子 */
}
}
return NULL;
}
TElemType Parent(BiPTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：若e是T的非根结点，则返回它的双亲，否则返回＂空＂*/
BiPTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a!=T) /* T中存在结点e且e是非根结点 */
return a->parent->data; /* 返回e的双亲的值 */
}
return Nil; /* 其余情况返回空 */
}
TElemType LeftChild(BiPTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点。操作结果：返回e的左孩子。若e无左孩子,则返回"空" */
BiPTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a->lchild) /* T中存在结点e且e存在左孩子 */
return a->lchild->data; /* 返回e的左孩子的值 */
}
return Nil; /* 其余情况返回空 */
}
TElemType RightChild(BiPTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点。操作结果：返回e的右孩子。若e无右孩子,则返回"空" */
BiPTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a->rchild) /* T中存在结点e且e存在右孩子 */
return a->rchild->data; /* 返回e的右孩子的值 */
}
return Nil; /* 其余情况返回空 */
}
TElemType LeftSibling(BiPTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：返回e的左兄弟。若e是T的左孩子或无左兄弟，则返回＂空＂*/
BiPTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a!=T&&a->parent->lchild&&a->parent->lchild!=a) /* T中存在结点e且e存在左兄弟 */
return a->parent->lchild->data; /* 返回e的左兄弟的值 */
}
return Nil; /* 其余情况返回空 */
}
TElemType RightSibling(BiPTree T,TElemType e)
{ /* 初始条件：二叉树T存在，e是T中某个结点 */
/* 操作结果：返回e的右兄弟。若e是T的右孩子或无右兄弟，则返回＂空＂*/
BiPTree a;
if(T) /* 非空树 */
{
a=Point(T,e); /* a是结点e的指针 */
if(a&&a!=T&&a->parent->rchild&&a->parent->rchild!=a) /* T中存在结点e且e存在右兄弟 */
return a->parent->rchild->data; /* 返回e的右兄弟的值 */
}
return Nil; /* 其余情况返回空 */
}
Status InsertChild(BiPTree p,int LR,BiPTree c) /* 形参T无用 */
{ /* 初始条件：二叉树T存在，p指向T中某个结点，LR为0或1，非空二叉树c与T不相交且右子树为空 */
/* 操作结果：根据LR为0或1，插入c为T中p所指结点的左或右子树。p所指结点 */
/*           的原有左或右子树则成为c的右子树 */
if(p) /* p不空 */
{
if(LR==0)
{
c->rchild=p->lchild;
if(c->rchild) /* c有右孩子(p原有左孩子) */
c->rchild->parent=c;
p->lchild=c;
c->parent=p;
}
else /* LR==1 */
{
c->rchild=p->rchild;
if(c->rchild) /* c有右孩子(p原有右孩子) */
c->rchild->parent=c;
p->rchild=c;
c->parent=p;
}
return OK;
}
return ERROR; /* p空 */
}
Status DeleteChild(BiPTree p,int LR) /* 形参T无用 */
{ /* 初始条件：二叉树T存在，p指向T中某个结点，LR为0或1 */
/* 操作结果：根据LR为0或1，删除T中p所指结点的左或右子树 */
if(p) /* p不空 */
{
if(LR==0) /* 删除左子树 */
ClearBiTree(&p->lchild);
else /* 删除右子树 */
ClearBiTree(&p->rchild);
return OK;
}
return ERROR; /* p空 */
}
void PreOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
{ /* 先序递回遍历二叉树T */
if(T)
{
Visit(T); /* 先访问根结点 */
PreOrderTraverse(T->lchild,Visit); /* 再先序遍历左子树 */
PreOrderTraverse(T->rchild,Visit); /* 最后先序遍历右子树 */
}
}
void InOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
{ /* 中序递回遍历二叉树T */
if(T)
{
InOrderTraverse(T->lchild,Visit); /* 中序遍历左子树 */
Visit(T); /* 再访问根结点 */
InOrderTraverse(T->rchild,Visit); /* 最后中序遍历右子树 */
}
}
void PostOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
{ /* 后序递回遍历二叉树T */
if(T)
{
PostOrderTraverse(T->lchild,Visit); /* 后序遍历左子树 */
PostOrderTraverse(T->rchild,Visit); /* 后序遍历右子树 */
Visit(T); /* 最后访问根结点 */
}
}
void LevelOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
{ /* 层序遍历二叉树T(利用伫列) */
LinkQueue q;
QElemType a;
if(T)
{
InitQueue(&q);
EnQueue(&q,T);
while(!QueueEmpty(q))
{
DeQueue(&q,&a);
Visit(a);
if(a->lchild!=NULL)
EnQueue(&q,a->lchild);
if(a->rchild!=NULL)
EnQueue(&q,a->rchild);
}
}
}
== 遍历 ==
我们经常希望访问树中的每一个结点并且查看它的值。有很多常见的顺序来访问所有的结点，而且每一种都有有用的性质。
=== 深度优先遍历 ===
在深度优先顺序中，我们希望从根结点访问最远的结点。和图的深度优先搜索不同的是，不需记住访问过的每一个结点，因为树中不会有环。前序，中序和后序遍历都是深度优先遍历的特例。
==== 前（先）序、中序、后序遍历 ====
遍历二叉树：L、D、R分别表示遍历左子树、访问根结点和遍历右子树，则先(根)序遍历二叉树的顺序是DLR，中(根)序遍历二叉树的顺序是LDR，后(根)序遍历二叉树的顺序是LRD。还有按层遍历二叉树。这些方法的时间复杂度都是O(n)，n为结点个数。
如果T2是由有序树T转换而来的二叉树，那么T中结点的前序就是T2中结点的前序，T中结点的后序就是T2中结点的中序。任何一棵二叉树的叶结点在先序、中序和后序遍历中的相对次序不发改变。设n,m为一棵二叉树上的两个结点，在中序遍历时，n在m前的条件是n在m的左方。前序序列和中序序列相同的二叉树为空树或任一结点均无左孩子的非空二叉树；中序序列和后序序列相同的二叉树为空树或任一结点均无右孩子的非空二叉树；前序序列和后序序列相同的二叉树为空树或仅有一个结点的二叉树。
假设我们有一个包含值的value和指向两个子结点的left和right的树结点结构。我们可以写出这样的过程：
visit(node)
print node.value
if node.left
= null then visit(node.left)
if node.right != null then visit(node.right)
这样会用前序打印出树中的值。在前序，每个结点在访问它的子结点之前访问。类似地，如果打印语句在最后，每个结点在访问他的子节点之后访问，树中的值会用后序来打印。在这两种情况中，左子树中的值比右子树中得值先打印。
visit(node)
if node.left
= null then visit(node.left)
print node.value
if node.right != null then visit(node.right)
最后，上面的中序遍历，每个结点在访问左子树和右子树之间访问。这在遍历二叉搜寻树时很常用，因为它能用递增的顺序来遍历所有的值。
为什么呢？如果n是二叉搜寻树的结点，那么n的左子树的所有结点的值都比n的值要小，而且n的右子树的所有节点的值都比n的值要大。因此，如果我们顺序遍历左子树，然后访问n，然后顺序遍历右子树。我们就已经循序存取了整个树。
后序遍历伪代码如下：
visit(node)
if node.left
= null then visit(node.left)
if node.right != null then visit(node.right)
print node.value
以上的递回演算法使用与树的高度成比例的栈空间。如果我们在每个结点中存储指向父结点的指标，那样可以使用反复运算演算法，只使用常数空间实现所有这些遍历。然而，指向父结点的指标占用更多的空间。这只在需要指向父节点的指标或栈空间有限时才使用。例如，
这是一个中序遍历的反复运算演算法：
visit(root)
prev    := null
current := root
next    := null
while current != null
if prev == current.parent
prev := current
next := current.left
if next == null or prev == current.left
print current.value
prev := current
next := current.right
if next == null or prev == current.right
prev := current
next := current.parent
current := next
=== 广度优先遍历 ===
和深度优先遍历不同，广度优先遍历会先访问离根节点最近的节点。二叉树的广度优先遍历又称按层次遍历。演算法借助伫列实现。
== 转换自多叉树 ==
一般有序树可映射为二叉树，但反之未必成立。
n叉树转换为二叉树的方法：二叉树中结点x的左子结点为n叉树中结点x的左子结点；二叉树中结点x的右子结点为n叉树中结点x的第一个右边的同级结点y。
二叉树当且仅当根节点没有右子结点时可转换为n叉树。
例如，在左边的树中，A有6个子结点{B,C,D,E,F,G}。它能被转换成右边的二叉树。
将n叉树转换为二叉树的例子
*将一棵树转换为二叉树的方法：
#在兄弟之间加一连线；
#对每个结点，除了其左孩子外，去除其与其余孩子之间的联系；
#以树的根结点为轴心，将整树顺时针转45度。
=== 树的二叉链表标记法（孩子兄弟标记法） ===
树的二叉链表标记法（孩子兄弟标记法）是树和二叉树转换的媒介。
==== 存储结构 ====
二叉树与森林相互转换的逻辑示意
/* 树的二叉链表(孩子—兄弟)存储表示 */
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;
==== 基本操作 ====
/* 树的二叉链表(孩子—兄弟)存储的基本操作(17个) */
#define ClearTree DestroyTree /* 二者操作相同 */
#include"func6-2.c" /* 包括PreOrderTraverse() */
void InitTree(CSTree *T)
{ /* 操作结果：构造空树T */
*T=NULL;
}
void DestroyTree(CSTree *T)
{ /* 初始条件：树T存在。操作结果：销毁树T */
if(*T)
{
if((*T)->firstchild) /* T有长子 */
DestroyTree(&(*T)->firstchild); /* 销毁T的长子为根结点的子树 */
if((*T)->nextsibling) /* T有下一个兄弟 */
DestroyTree(&(*T)->nextsibling); /* 销毁T的下一个兄弟为根结点的子树 */
free(*T); /* 释放根结点 */
*T=NULL;
}
}
typedef CSTree QElemType; /* 定义伫列元素类型 */
#include"c3-2.h" /* 定义LinkQueue类型(链伫列) */
#include"bo3-2.c" /* LinkQueue类型的基本操作 */
void CreateTree(CSTree *T)
{ /* 构造树T */
char c20; /* 临时存放孩子结点(设不超过20个)的值 */
CSTree p,p1;
LinkQueue q;
int i,l;
InitQueue(&q);
printf("请输入根结点(字元型,空格为空): ");
scanf("%c%*c",&c0);
if(c0!=Nil) /* 非空树 */
{
*T=(CSTree)malloc(sizeof(CSNode)); /* 建立根结点 */
(*T)->data=c0;
(*T)->nextsibling=NULL;
EnQueue(&q,*T); /* 入队根结点的指针 */
while(!QueueEmpty(q)) /* 队不空 */
{
DeQueue(&q,&p); /* 出队一个结点的指标 */
printf("请按长幼顺序输入结点%c的所有孩子: ",p->data);
gets(c);
l=strlen(c);
if(l>0) /* 有孩子 */
{
p1=p->firstchild=(CSTree)malloc(sizeof(CSNode)); /* 建立长子结点 */
p1->data=c0;
for(i=1;inextsibling=(CSTree)malloc(sizeof(CSNode)); /* 建立下一个兄弟结点 */
EnQueue(&q,p1); /* 入队上一个结点 */
p1=p1->nextsibling;
p1->data=ci;
}
p1->nextsibling=NULL;
EnQueue(&q,p1); /* 入队最后一个结点 */
}
else
p->firstchild=NULL; /* 长子指针为空 */
}
}
else
*T=NULL; /* 空树 */
}
Status TreeEmpty(CSTree T)
{ /* 初始条件：树T存在。操作结果：若T为空树，则返回TURE，否则返回FALSE */
if(T) /* T不空 */
return FALSE;
else
return TRUE;
}
int TreeDepth(CSTree T)
{ /* 初始条件：树T存在。操作结果：返回T的深度 */
CSTree p;
int depth,max=0;
if(!T) /* 树空 */
return 0;
if(!T->firstchild) /* 树无长子 */
return 1;
for(p=T->firstchild;p;p=p->nextsibling)
{ /* 求子树深度的最大值 */
depth=TreeDepth(p);
if(depth>max)
max=depth;
}
return max+1; /* 树的深度=子树深度最大值+1 */
}
TElemType Value(CSTree p)
{ /* 返回p所指结点的值 */
return p->data;
}
TElemType Root(CSTree T)
{ /* 初始条件：树T存在。操作结果：返回T的根 */
if(T)
return Value(T);
else
return Nil;
}
CSTree Point(CSTree T,TElemType s)
{ /* 返回二叉链表(孩子—兄弟)树T中指向元素值为s的结点的指标。另加 */
LinkQueue q;
QElemType a;
if(T) /* 非空树 */
{
InitQueue(&q); /* 初始化伫列 */
EnQueue(&q,T); /* 根结点入队 */
while(!QueueEmpty(q)) /* 队不空 */
{
DeQueue(&q,&a); /* 出队,伫列元素赋给a */
if(a->data==s)
return a;
if(a->firstchild) /* 有长子 */
EnQueue(&q,a->firstchild); /* 入队长子 */
if(a->nextsibling) /* 有下一个兄弟 */
EnQueue(&q,a->nextsibling); /* 入队下一个兄弟 */
}
}
return NULL;
}
Status Assign(CSTree *T,TElemType cur_e,TElemType value)
{ /* 初始条件：树T存在，cur_e是树T中结点的值。操作结果：改cur_e为value */
CSTree p;
if(*T) /* 非空树 */
{
p=Point(*T,cur_e); /* p为cur_e的指针 */
if(p) /* 找到cur_e */
{
p->data=value; /* 赋新值 */
return OK;
}
}
return ERROR; /* 树空或没找到 */
}
TElemType Parent(CSTree T,TElemType cur_e)
{ /* 初始条件：树T存在，cur_e是T中某个结点 */
/* 操作结果：若cur_e是T的非根结点，则返回它的双亲，否则函数值为＂空＂*/
CSTree p,t;
LinkQueue q;
InitQueue(&q);
if(T) /* 树非空 */
{
if(Value(T)==cur_e) /* 根结点值为cur_e */
return Nil;
EnQueue(&q,T); /* 根结点入队 */
while(!QueueEmpty(q))
{
DeQueue(&q,&p);
if(p->firstchild) /* p有长子 */
{
if(p->firstchild->data==cur_e) /* 长子为cur_e */
return Value(p); /* 返回双亲 */
t=p; /* 双亲指针赋给t */
p=p->firstchild; /* p指向长子 */
EnQueue(&q,p); /* 入队长子 */
while(p->nextsibling) /* 有下一个兄弟 */
{
p=p->nextsibling; /* p指向下一个兄弟 */
if(Value(p)==cur_e) /* 下一个兄弟为cur_e */
return Value(t); /* 返回双亲 */
EnQueue(&q,p); /* 入队下一个兄弟 */
}
}
}
}
return Nil; /* 树空或没找到cur_e */
}
TElemType LeftChild(CSTree T,TElemType cur_e)
{ /* 初始条件：树T存在，cur_e是T中某个结点 */
/* 操作结果：若cur_e是T的非叶子结点，则返回它的最左孩子，否则返回＂空＂*/
CSTree f;
f=Point(T,cur_e); /* f指向结点cur_e */
if(f&&f->firstchild) /* 找到结点cur_e且结点cur_e有长子 */
return f->firstchild->data;
else
return Nil;
}
TElemType RightSibling(CSTree T,TElemType cur_e)
{ /* 初始条件：树T存在，cur_e是T中某个结点 */
/* 操作结果：若cur_e有右兄弟，则返回它的右兄弟，否则返回＂空＂*/
CSTree f;
f=Point(T,cur_e); /* f指向结点cur_e */
if(f&&f->nextsibling) /* 找到结点cur_e且结点cur_e有右兄弟 */
return f->nextsibling->data;
else
return Nil; /* 树空 */
}
Status InsertChild(CSTree *T,CSTree p,int i,CSTree c)
{ /* 初始条件：树T存在，p指向T中某个结点，1≦i≦p所指结点的度+1，非空树c与T不相交 */
/* 操作结果：插入c为T中p结点的第i棵子树 */
/* 因为p所指结点的位址不会改变，故p不需是参考类型 */
int j;
if(*T) /* T不空 */
{
if(i==1) /* 插入c为p的长子 */
{
c->nextsibling=p->firstchild; /* p的原长子现是c的下一个兄弟(c本无兄弟) */
p->firstchild=c;
}
else /* 找插入点 */
{
p=p->firstchild; /* 指向p的长子 */
j=2;
while(p&&i>j)
{
p=p->nextsibling;
j++;
}
if(j==i) /* 找到插入位置 */
{
c->nextsibling=p->nextsibling;
p->nextsibling=c;
}
else /* p原有孩子数小于i-1 */
return ERROR;
}
return OK;
}
else /* T空 */
return ERROR;
}
Status DeleteChild(CSTree *T,CSTree p,int i)
{ /* 初始条件：树T存在，p指向T中某个结点，1≦i≦p所指结点的度 */
/* 操作结果：删除T中p所指结点的第i棵子树 */
/* 因为p所指结点的位址不会改变，故p不需是参考类型 */
CSTree b;
int j;
if(*T) /* T不空 */
{
if(i==1) /* 删除长子 */
{
b=p->firstchild;
p->firstchild=b->nextsibling; /* p的原次子现是长子 */
b->nextsibling=NULL;
DestroyTree(&b);
}
else /* 删除非长子 */
{
p=p->firstchild; /* p指向长子 */
j=2;
while(p&&i>j)
{
p=p->nextsibling;
j++;
}
if(j==i) /* 找到第i棵子树 */
{
b=p->nextsibling;
p->nextsibling=b->nextsibling;
b->nextsibling=NULL;
DestroyTree(&b);
}
else /* p原有孩子数小于i */
return ERROR;
}
return OK;
}
else
return ERROR;
}
void PostOrderTraverse(CSTree T,void(*Visit)(TElemType))
{ /* 后根遍历孩子—兄弟二叉链表结构的树T */
CSTree p;
if(T)
{
if(T->firstchild) /* 有长子 */
{
PostOrderTraverse(T->firstchild,Visit); /* 后根遍历长子子树 */
p=T->firstchild->nextsibling; /* p指向长子的下一个兄弟 */
while(p)
{
PostOrderTraverse(p,Visit); /* 后根遍历下一个兄弟子树 */
p=p->nextsibling; /* p指向再下一个兄弟 */
}
}
Visit(Value(T)); /* 最后访问根结点 */
}
}
void LevelOrderTraverse(CSTree T,void(*Visit)(TElemType))
{ /* 层序遍历孩子—兄弟二叉链表结构的树T */
CSTree p;
LinkQueue q;
InitQueue(&q);
if(T)
{
Visit(Value(T)); /* 先访问根结点 */
EnQueue(&q,T); /* 入队根结点的指针 */
while(!QueueEmpty(q)) /* 队不空 */
{
DeQueue(&q,&p); /* 出队一个结点的指标 */
if(p->firstchild) /* 有长子 */
{
p=p->firstchild;
Visit(Value(p)); /* 访问长子结点 */
EnQueue(&q,p); /* 入队长子结点的指针 */
while(p->nextsibling) /* 有下一个兄弟 */
{
p=p->nextsibling;
Visit(Value(p)); /* 访问下一个兄弟 */
EnQueue(&q,p); /* 入队兄弟结点的指针 */
}
}
}
}
}
== 线索二叉树 ==
线索二叉树（threaded binary tree，保留遍历时结点在任一序列的前驱和后继的资讯）：若结点有左子树，则其lchild域指示其左孩子，否则令lchild域指示其前驱；若结点有右子树，则其rchild域指示其右孩子，否则令rchild指示其后继。还需在结点结构中增加两个标志域LTag和RTag。LTag=0时，lchild域指示结点的左孩子，LTag=1时，lchild域指示结点的前驱；RTag=0时，rchild域指示结点的右孩子，RTag=1时，rchild域指示结点的后继。以这种结点结构构成的二叉链表作为二叉树的存储结构，叫做线索链表，其中指向结点前驱和后继的指标叫做线索，加上线索的二叉树称为线索二叉树。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。若对二叉树进行中序遍历，则所得的线索二叉树称为中序线索二叉树，线索链表称为为中序线索链表。线索二叉树是一种物理结构。
File:tbt1.jpg
在中序线索树找结点后继的规律是：若其右标志为1，则右链为线索，指示其后继，否则遍历其右子树时访问的第一个结点（右子树最左下的结点）为其后继；找结点前驱的规律是：若其左标志为1，则左链为线索，指示其前驱，否则遍历左子树时最后访问的一个结点（左子树中最右下的结点）为其前驱。
在后序线索树中找到结点的后继分三种情况：
#若结点是二叉树的根，则其后继为空；
#若结点是其双亲的右孩子，或是其双亲的左孩子且其双亲没有右子树，则其后继即为双亲结点；
#若结点是其双亲的左孩子，且其双亲有右子树，则其后继为双亲右子树上按后序遍历列出的第一个结点。
=== 二叉线索存储表示 ===
==== 存储结构 ====
二叉树的二叉线索存储表示：在线索链表上添加一个头结点，并令其lchild域的指标指向二叉树的根结点，其rchild域的指标指向中序遍历时访问的最后一个结点。令二叉树中序序列中的第一个结点的lchild域指标和最后一个结点的rchild域的指标均指向头结点，这样就建立了一个双向线索链表。二叉树常采用二叉链表方式存储。
/* 二叉树的二叉线索存储表示 */
typedef enum{Link,Thread}PointerTag; /* Link(0)：指针，Thread(1)：线索 */
typedef struct BiThrNode
{
TElemType data;
struct BiThrNode *lchild,*rchild; /* 左右孩子指针 */
PointerTag LTag,RTag; /* 左右标志 */
}BiThrNode,*BiThrTree;
==== 基本操作 ====
/* 二叉树的二叉线索存储的基本操作 */
void CreateBiThrTree(BiThrTree *T)
{ /* 按先序输入线索二叉树中结点的值，构造线索二叉树T。0(整型)/空格(字元型)表示空结点 */
TElemType ch;
scanf(form,&ch);
if(ch==Nil)
*T=NULL;
else
{
*T=(BiThrTree)malloc(sizeof(BiThrNode)); /* 生成根结点(先序) */
if(!*T)
exit(OVERFLOW);
(*T)->data=ch; /* 给根结点赋植 */
CreateBiThrTree(&(*T)->lchild); /* 递回构造左子树 */
if((*T)->lchild) /* 有左孩子 */
(*T)->LTag=Link; /* 给左标志赋值(指标) */
CreateBiThrTree(&(*T)->rchild); /* 递回构造右子树 */
if((*T)->rchild) /* 有右孩子 */
(*T)->RTag=Link; /* 给右标志赋值(指标) */
}
}
BiThrTree pre; /* 全域变数，始终指向刚刚访问过的结点 */
void InThreading(BiThrTree p)
{ /* 通过中序遍历进行中序线索化，线索化之后pre指向最后一个结点。演算法6.7 */
if(p) /* 线索二叉树不空 */
{
InThreading(p->lchild); /* 递回左子树线索化 */
if(!p->lchild) /* 没有左孩子 */
{
p->LTag=Thread; /* 左标志为线索(前驱) */
p->lchild=pre; /* 左孩子指标指向前驱 */
}
if(pre && !pre->rchild) /* 前驱不为空并且前驱没有右孩子 */
{
pre->RTag=Thread; /* 前驱的右标志为线索(后继) */
pre->rchild=p; /* 前驱右孩子指标指向其后继(当前结点p) */
}
pre=p; /* 保持pre指向p的前驱 */
InThreading(p->rchild); /* 递回右子树线索化 */
}
}
void InOrderThreading(BiThrTree *Thrt,BiThrTree T)
{ /* 中序遍历二叉树T，并将其中序线索化，Thrt指向头结点。演算法6.6 */
*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; /* pre(前驱)的初值指向头结点 */
InThreading(T); /* 中序遍历进行中序线索化，pre指向中序遍历的最后一个结点 */
pre->rchild=*Thrt; /* 最后一个结点的右指标指向头结点 */
pre->RTag=Thread; /* 最后一个结点的右标志为线索 */
(*Thrt)->rchild=pre; /* 头结点的右指标指向中序遍历的最后一个结点 */
}
}
void InOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType))
{ /* 中序遍历线索二叉树T(头结点)的非递回演算法。演算法6.5 */
BiThrTree p;
p=T->lchild; /* p指向根结点 */
while(p!=T)
{ /* 空树或遍历结束时，p==T */
while(p->LTag==Link) /* 由根结点一直找到二叉树的最左结点 */
p=p->lchild;
Visit(p->data); /* 访问此结点 */
while(p->RTag==Thread&&p->rchild!=T) /* p->rchild是线索(后继)，且不是遍历的最后一个结点 */
{
p=p->rchild;
Visit(p->data); /* 访问后继结点 */
}
p=p->rchild; /* 若p->rchild不是线索(是右孩子)，p指向右孩子，返回回圈，*/
}              /* 找这棵子树中序遍历的第1个结点 */
}
void PreThreading(BiThrTree p)
{ /* PreOrderThreading()调用的递回函数 */
if(!pre->rchild) /* p的前驱没有右孩子 */
{
pre->rchild=p; /* p前驱的后继指向p */
pre->RTag=Thread; /* pre的右孩子为线索 */
}
if(!p->lchild) /* p没有左孩子 */
{
p->LTag=Thread; /* p的左孩子为线索 */
p->lchild=pre; /* p的左孩子指向前驱 */
}
pre=p; /* 移动前驱 */
if(p->LTag==Link) /* p有左孩子 */
PreThreading(p->lchild); /* 对p的左孩子递回呼叫preThreading() */
if(p->RTag==Link) /* p有右孩子 */
PreThreading(p->rchild); /* 对p的右孩子递回呼叫preThreading() */
}
void PreOrderThreading(BiThrTree *Thrt,BiThrTree T)
{ /* 先序线索化二叉树T，头结点的右指标指向先序遍历的最后1个结点 */
*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; /* 前驱为头结点 */
PreThreading(T); /* 从头结点开始先序递回线索化 */
pre->rchild=*Thrt; /* 最后一个结点的后继指向头结点 */
pre->RTag=Thread;
(*Thrt)->rchild=pre; /* 头结点的后继指向最后一个结点 */
}
}
void PreOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType))
{ /* 先序遍历线索二叉树T(头结点)的非递回演算法 */
BiThrTree p=T->lchild; /* p指向根结点 */
while(p!=T) /* p没指向头结点(遍历的最后1个结点的后继指向头结点) */
{
Visit(p->data); /* 访问根结点 */
if(p->LTag==Link) /* p有左孩子 */
p=p->lchild; /* p指向左孩子(后继) */
else /* p无左孩子 */
p=p->rchild; /* p指向右孩子或后继 */
}
}
void PostThreading(BiThrTree p)
{ /* PostOrderThreading()调用的递回函数 */
if(p) /* p不空 */
{
PostThreading(p->lchild); /* 对p的左孩子递回呼叫PostThreading() */
PostThreading(p->rchild); /* 对p的右孩子递回呼叫PostThreading() */
if(!p->lchild) /* p没有左孩子 */
{
p->LTag=Thread; /* p的左孩子为线索 */
p->lchild=pre; /* p的左孩子指向前驱 */
}
if(!pre->rchild) /* p的前驱没有右孩子 */
{
pre->RTag=Thread; /* p前驱的右孩子为线索 */
pre->rchild=p; /* p前驱的后继指向p */
}
pre=p; /* 移动前驱 */
}
}
void PostOrderThreading(BiThrTree *Thrt,BiThrTree T)
{ /* 后序递回线索化二叉树 */
*Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
if(!*Thrt) /* 生成头结点 */
exit(OVERFLOW);
(*Thrt)->LTag=Link; /* 头结点的左指针为孩子 */
(*Thrt)->RTag=Thread; /* 头结点的右指针为线索 */
if(!T) /* 空树 */
(*Thrt)->lchild=(*Thrt)->rchild=*Thrt; /* 头结点的左右指标指向自身 */
else
{ /* 非空树 */
(*Thrt)->lchild=(*Thrt)->rchild=T; /* 头结点的左右指标指向根结点(最后一个结点) */
pre=*Thrt; /* 前驱为头结点 */
PostThreading(T); /* 从头结点开始后序递回线索化 */
if(pre->RTag!=Link) /* 最后一个结点没有右孩子 */
{
pre->rchild=*Thrt; /* 最后一个结点的后继指向头结点 */
pre->RTag=Thread;
}
}
}
void DestroyBiTree(BiThrTree *T)
{ /* DestroyBiThrTree调用的递回函数，T指向根结点 */
if(*T) /* 非空树 */
{
if((*T)->LTag==0) /* 有左孩子 */
DestroyBiTree(&(*T)->lchild); /* 销毁左孩子子树 */
if((*T)->RTag==0) /* 有右孩子 */
DestroyBiTree(&(*T)->rchild); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
T=NULL; /* 空指针赋0 */
}
}
void DestroyBiThrTree(BiThrTree *Thrt)
{ /* 初始条件：线索二叉树Thrt存在。操作结果：销毁线索二叉树Thrt */
if(*Thrt) /* 头结点存在 */
{
if((*Thrt)->lchild) /* 根结点存在 */
DestroyBiTree(&(*Thrt)->lchild); /* 递回销毁头结点lchild所指二叉树 */
free(*Thrt); /* 释放头结点 */
*Thrt=NULL; /* 线索二叉树Thrt指针赋0 */
}
}