树
目录:
6.1 树的定义:
树(Tree)是n个结点的有限集。n=0时称为空树。在任意一棵非空树中:
(1)有且仅有一个特定的称为根(Root)的结点。
(2)当n<1时,其余结点可分为m个互不相交的有限集T1、T2…Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
- 度:结点拥有的子树数量。
- 叶结点、终端结点:度为0的结点。
- 非终端结点、分支结点:度不为0的结点。
- 树的度:树内各结点的度的最大值。
- 孩子:结点的子树的根。
- 兄弟:同一个双亲的孩子。
- 祖先:结点从根到该结点所经分支上的所有结点。
- 子孙:以某结点为根的子树的任一结点。
- 深度、高度:树中结点的最大层次。
- 有序树:将树中结点的各子树看成从左到右有次序的,不能互换的,否则为无序树。
- 森林:m棵互不相交的树的集合。
6.2 抽象数据类型:
ADT 树(tree)
Data
树是由一个根结点和若干棵子树构成。树中结点具有相同数据类型以及层次关系。
Operation
InitTree(*T):构造空树T
DestroyTree(*T):销毁树T
CreateTree(*T,definition):按照definition中给出的定义构造树
ClearTree(*T):若树T存在,将树T清为空树
TreeEmpty(T):若T为空树,返回true,否则返回false
TreeDepth(T):返回树的深度
Root(T):返回T的根节点
Value(T,cur_e):cur_e是树T中的一个结点,返回该结点的值
Assign(T,cur_e,value):给树T中的结点cur_e赋值为value
Parent(T,cur_e):若cur_e是树T的非根结点,则返回它的双亲,否则返回空
LeftChild(T,cur_e):若cur_e是树T的非叶结点,则返回它的左孩子,否则返回空
RightSibling(T,cur_e):若cur_e有右兄弟,则返回它的右兄弟,否则返回空
InsertChild(*T,*p,i,c):其中p指向树T的某个结点,i为所指结点p的度加上1,非空树c与T不相交,操作结果为插入c为树T中p指结点的第i棵子树
DeleteChild(*T,*p,i):其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指结点的第i棵子树
endADT
6.3 树的存储结构:
6.3.1 双亲表示法:
每个节点中,附设一个指示器指示其双亲结点到链表中的位置,如图所示:
/* 树的双亲表示法结点结构定义 */
#define MAX_TREE_SIZE 100 //定义树的大小
typedef int TElemType; //树结点的数据类型,暂定为整型
typedef struct PTNode //结点结构
{
TElemType data; //结点数据
int parent; //双亲位置
}PTNode;
typedef struct //树结构
{
PTNode nodes[MAX_TREE_SIZE]; //结点数组
int t, n; //根的位置和结点数
};
我们把根节点的双亲位置域设置为-1,如下图所示:
6.3.2 孩子表示法:
可以考虑用多重链表,每个结点有多个指针域,每个指针指向一棵子树的根结点,多重链表表示法。
但我们可以有更好的方法,既减少空指针的浪费,又能使结点结构相同。我们可以把每个结点的孩子结点排列起来,以单链表作存储结构,即n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。
这就是孩子表示法:
为此,设计两种结点结构,一个是孩子链表的孩子结点:
child为数据域,用来存储某个结点在表头数组中的下标。next为指针域,用来存储指向某结点的下一个孩子结点的指针。
另一个是表头数组的表头结点:
data为数据域,存储某结点的数据信息。firstchild是头指针域,存储该结点的孩子链表的头指针。
结构体代码:
/* 树的孩子表示法结构定义 */
typedef struct CTNode //孩子结点
{
int child;
struct CTNode* next;
}*ChildPtr;
typedef struct //表头结构
{
TElemType data;
ChildPtr firstchild;
}CTBox;
typedef struct //树结构
{
CTBox nodes[MAX_TREE_SIZE]; //结点数组
int r, n; //根的位置和结点数
};
6.3.3 孩子兄弟表示法:
设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。
data是数据域,firstchild为指针域,存储该结点的第一个孩子结点的存储地址,rightsib是指针域,存储该结点的右兄弟结点的存储地址。
结构体代码:
/* 树的孩子兄弟表示法结构定义 */
typedef struct CSNode
{
TElemType data;
struct CSNode* firstchild, * rightsib;
}CSNode,*CSTree;
6.4 二叉树:
二叉树(Binary Tree)是n个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
6.4.1 特点:
- 每个结点最多有两棵子树。(可以有一棵或没有)
- 左子树和右子树有顺序,次序不能颠倒。
- 即使只有一棵子树,也要区分左右子树。
五种形态,如下图所示:
6.4.2 特殊二叉树:
- 斜树:所有结点只有左子树——左斜树;所有结点只有右子树——右斜树。
- 满二叉树:在一棵二叉树中,所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
- 完全二叉树:对一棵具有n个结点的二叉树按层序编号,如果编号为i的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则称这课二叉树为完全二叉树。
对于完全二叉树:
- 叶子结点只能出现在最下两层
- 最下层的叶子一定集中在左部连续位置
- 倒数二层,若有叶子结点,一定都在右部连续位置
- 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况
- 同样结点数的二叉树,完全二叉树的深度最小。
6.4.3 性质:
- 在二叉树的第i层上至多有2i-1个结点。
- 深度为k的二叉树至多有2k-1个结点
- 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2。
- 具有n个结点的完全二叉树的深度为log2n+1,向下取整
- 如果对一棵有n个结点的完全二叉树的结点按层序编号,对任一结点i有:
- 若i=1,则结点i是二叉树的根,无双亲;若i>1,则其双亲是i/2(向下取整)
- 若2i>n,则结点i无左孩子;否则左孩子是2i
- 若2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1
6.5 二叉树的存储结构:
6.5.1 顺序存储结构:
一般只用于完全二叉树,否则会产生大量的存储空间浪费:
存储一般的二叉树:
结构体代码:
typedef struct
{
int level, order; /* 结点的层,本层序号(按满二叉树计算) */
}Position;
6.5.2 链式存储结构:
设计一个数据域和两个指针域,因为二叉树每个结点最多有两个孩子。这样的链表称作二叉链表。
结构体代码:
typedef struct BiTNode /* 结点结构 */
{
TElemType data; /* 结点数据 */
struct BiTNode* lchild, * rchild; /* 左右孩子指针 */
}BiTNode, * BiTree;
结构示意图:
6.6 二叉树遍历:
**二叉树的遍历(tarversing binary tree)**是从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
6.6.1 前序遍历:
规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树,如图所示,遍历顺序为:AVDGHCEIF
顺序存储:
/* 初始条件: 二叉树存在 */
/* 操作结果: 先序遍历T。 */
Status PreOrderTraverse(SqBiTree T)
{
if (!BiTreeEmpty(T)) /* 树不空 */
PreTraverse(T, 0);
printf("\n");
return OK;
}
/* InOrderTraverse()调用 */
void InTraverse(SqBiTree T, int e)
{
if (T[2 * e + 1] != Nil) /* 左子树不空 */
InTraverse(T, 2 * e + 1);
visit(T[e]);
if (T[2 * e + 2] != Nil) /* 右子树不空 */
InTraverse(T, 2 * e + 2);
}
链式存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 前序递归遍历T */
void PreOrderTraverse(BiTree T)
{
if (T == NULL)
return;
printf("%c", T->data);/* 显示结点数据,可以更改为其它对结点操作 */
PreOrderTraverse(T->lchild); /* 再先序遍历左子树 */
PreOrderTraverse(T->rchild); /* 最后先序遍历右子树 */
}
6.6.2 中序遍历:
规则是若树为空,则空操作返回,否则从根节点开始,中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树,如图所示:遍历顺序为GDHBAEICF。
顺序存储:
/* 初始条件: 二叉树存在 */
/* 操作结果: 中序遍历T。 */
Status InOrderTraverse(SqBiTree T)
{
if (!BiTreeEmpty(T)) /* 树不空 */
InTraverse(T, 0);
printf("\n");
return OK;
}
/* PostOrderTraverse()调用 */
void PostTraverse(SqBiTree T, int e)
{
if (T[2 * e + 1] != Nil) /* 左子树不空 */
PostTraverse(T, 2 * e + 1);
if (T[2 * e + 2] != Nil) /* 右子树不空 */
PostTraverse(T, 2 * e + 2);
visit(T[e]);
}
链式存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 中序递归遍历T */
void InOrderTraverse(BiTree T)
{
if (T == NULL)
return;
InOrderTraverse(T->lchild); /* 中序遍历左子树 */
printf("%c", T->data);/* 显示结点数据,可以更改为其它对结点操作 */
InOrderTraverse(T->rchild); /* 最后中序遍历右子树 */
}
6.6.3 后序遍历:
规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点,如图所示,遍历顺序为:GHDBIEFCA。
顺序存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 后序遍历T。 */
Status PostOrderTraverse(SqBiTree T)
{
if (!BiTreeEmpty(T)) /* 树不空 */
PostTraverse(T, 0);
printf("\n");
return OK;
}
链式存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 后序递归遍历T */
void PostOrderTraverse(BiTree T)
{
if (T == NULL)
return;
PostOrderTraverse(T->lchild); /* 先后序遍历左子树 */
PostOrderTraverse(T->rchild); /* 再后序遍历右子树 */
printf("%c", T->data);/* 显示结点数据,可以更改为其它对结点操作 */
}
6.6.4 层序遍历:
规则是若树为空,则空操作返回,否则从树的第一层,也就是根节点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问,如图所示,遍历顺序为:ABCDEFGHI。
6.7 二叉树基本操作:
6.7.1 二叉树建立:
顺序存储:
/* 构造空二叉树T。因为T是固定数组,不会改变,故不需要& */
Status InitBiTree(SqBiTree T)
{
int i;
for (i = 0; i < MAX_TREE_SIZE; i++)
T[i] = Nil; /* 初值为空 */
return OK;
}
/* 按层序次序输入二叉树中结点的值(字符型或整型), 构造顺序存储的二叉树T */
Status CreateBiTree(SqBiTree T)
{
int i = 0;
printf("请按层序输入结点的值(整型),0表示空结点,输999结束。结点数≤%d:\n", MAX_TREE_SIZE);
while (i < 10)
{
T[i] = i + 1;
if (i != 0 && T[(i + 1) / 2 - 1] == Nil && T[i] != Nil) /* 此结点(不空)无双亲且不是根 */
{
printf("出现无双亲的非根结点%d\n", T[i]);
exit(ERROR);
}
i++;
}
while (i < MAX_TREE_SIZE)
{
T[i] = Nil; /* 将空赋值给T的后面的结点 */
i++;
}
return OK;
}
链式存储:
/* 构造空二叉树T */
Status InitBiTree(BiTree* T)
{
*T = NULL;
return OK;
}
/* 按前序输入二叉树中结点的值(一个字符) */
/* #表示空树,构造二叉链表表示二叉树T。 */
void CreateBiTree(BiTree* T)
{
TElemType ch;
/* scanf("%c",&ch); */
ch = str[index++];
if (ch == '#')
*T = NULL;
else
{
*T = (BiTree)malloc(sizeof(BiTNode));
if (!*T)
exit(OVERFLOW);
(*T)->data = ch; /* 生成根结点 */
CreateBiTree(&(*T)->lchild); /* 构造左子树 */
CreateBiTree(&(*T)->rchild); /* 构造右子树 */
}
}
6.7.2 判断是否为空二叉树:
顺序存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 若T为空二叉树,则返回TRUE,否则FALSE */
Status BiTreeEmpty(SqBiTree T)
{
if (T[0] == Nil) /* 根结点为空,则树空 */
return TRUE;
else
return FALSE;
}
链式存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 若T为空二叉树,则返回TRUE,否则FALSE */
Status BiTreeEmpty(BiTree T)
{
if (T)
return FALSE;
else
return TRUE;
}
6.7.3 获取二叉树深度:
顺序存储:
/* 初始条件: 二叉树T存在。操作结果: 返回T的深度 */
int BiTreeDepth(SqBiTree T)
{
int i, j = -1;
for (i = MAX_TREE_SIZE - 1; i >= 0; i--) /* 找到最后一个结点 */
if (T[i] != Nil)
break;
i++;
do
j++;
while (i >= powl(2, j));/* 计算2的j次幂。 */
return j;
}
链式存储:
/* 初始条件: 二叉树T存在。操作结果: 返回T的深度 */
int BiTreeDepth(BiTree T)
{
int i, j;
if (!T)
return 0;
if (T->lchild)
i = BiTreeDepth(T->lchild);
else
i = 0;
if (T->rchild)
j = BiTreeDepth(T->rchild);
else
j = 0;
return i > j ? i + 1 : j + 1;
}
6.7.4 返回二叉树的根:
顺序存储:
/* 初始条件: 二叉树T存在 */
/* 操作结果: 当T不空,用e返回T的根,返回OK;否则返回ERROR,e无定义 */
Status Root(SqBiTree T, TElemType* e)
{
if (BiTreeEmpty(T)) /* T空 */
return ERROR;
else
{
*e = T[0];
return OK;
}
}
链式存储:
/* 初始条件: 二叉树T存在。操作结果: 返回T的根 */
TElemType Root(BiTree T)
{
if (BiTreeEmpty(T))
return Nil;
else
return T->data;
}
6.7.5 返回结点的值:
顺序存储:
/* 初始条件: 二叉树T存在,e是T中某个结点(的位置) */
/* 操作结果: 返回处于位置e(层,本层序号)的结点的值 */
TElemType Value(SqBiTree T, Position e)
{
return T[(int)powl(2, e.level - 1) + e.order - 2];
}
链式存储:
/* 初始条件: 二叉树T存在,p指向T中某个结点 */
/* 操作结果: 返回p所指结点的值 */
TElemType Value(BiTree p)
{
return p->data;
}
6.7.6 赋值:
顺序存储:
/* 初始条件: 二叉树T存在,e是T中某个结点(的位置) */
/* 操作结果: 给处于位置e(层,本层序号)的结点赋新值value */
Status Assign(SqBiTree T, Position e, TElemType value)
{
int i = (int)powl(2, e.level - 1) + e.order - 2; /* 将层、本层序号转为矩阵的序号 */
if (value != Nil && T[(i + 1) / 2 - 1] == Nil) /* 给叶子赋非空值但双亲为空 */
return ERROR;
else if (value == Nil && (T[i * 2 + 1] != Nil || T[i * 2 + 2] != Nil)) /* 给双亲赋空值但有叶子(不空) */
return ERROR;
T[i] = value;
return OK;
}
链式存储:
/* 给p所指结点赋值为value */
void Assign(BiTree p, TElemType value)
{
p->data = value;
}
6.7.7 返回双亲:
顺序存储:
/* 初始条件: 二叉树T存在,e是T中某个结点 */
/* 操作结果: 若e是T的非根结点,则返回它的双亲,否则返回"空" */
TElemType Parent(SqBiTree T, TElemType e)
{
int i;
if (T[0] == Nil) /* 空树 */
return Nil;
for (i = 1; i <= MAX_TREE_SIZE - 1; i++)
if (T[i] == e) /* 找到e */
return T[(i + 1) / 2 - 1];
return Nil; /* 没找到e */
}
6.7.8 返回左右孩子:
顺序存储:
/* 初始条件: 二叉树T存在,e是T中某个结点 */
/* 操作结果: 返回e的左孩子。若e无左孩子,则返回"空" */
TElemType LeftChild(SqBiTree T, TElemType e)
{
int i;
if (T[0] == Nil) /* 空树 */
return Nil;
for (i = 0; i <= MAX_TREE_SIZE - 1; i++)
if (T[i] == e) /* 找到e */
return T[i * 2 + 1];
return Nil; /* 没找到e */
}
/* 初始条件: 二叉树T存在,e是T中某个结点 */
/* 操作结果: 返回e的右孩子。若e无右孩子,则返回"空" */
TElemType RightChild(SqBiTree T, TElemType e)
{
int i;
if (T[0] == Nil) /* 空树 */
return Nil;
for (i = 0; i <= MAX_TREE_SIZE - 1; i++)
if (T[i] == e) /* 找到e */
return T[i * 2 + 2];
return Nil; /* 没找到e */
}
6.7.9 返回左右兄弟:
顺序存储:
/* 初始条件: 二叉树T存在,e是T中某个结点 */
/* 操作结果: 返回e的左兄弟。若e是T的左孩子或无左兄弟,则返回"空" */
TElemType LeftSibling(SqBiTree T, TElemType e)
{
int i;
if (T[0] == Nil) /* 空树 */
return Nil;
for (i = 1; i <= MAX_TREE_SIZE - 1; i++)
if (T[i] == e && i % 2 == 0) /* 找到e且其序号为偶数(是右孩子) */
return T[i - 1];
return Nil; /* 没找到e */
}
/*初始条件: 二叉树T存在,e是T中某个结点 */
/* 操作结果: 返回e的右兄弟。若e是T的右孩子或无右兄弟,则返回"空" */
TElemType RightSibling(SqBiTree T, TElemType e)
{
int i;
if (T[0] == Nil) /* 空树 */
return Nil;
for (i = 1; i <= MAX_TREE_SIZE - 1; i++)
if (T[i] == e && i % 2) /* 找到e且其序号为奇数(是左孩子) */
return T[i + 1];
return Nil; /* 没找到e */
}
6.7.10 清空二叉树:
顺序存储:
Status InitBiTree(SqBiTree T)
{
int i;
for (i = 0; i < MAX_TREE_SIZE; i++)
T[i] = Nil; /* 初值为空 */
return OK;
}
链式存储:
/* 初始条件: 二叉树T存在。操作结果: 销毁二叉树T */
void DestroyBiTree(BiTree* T)
{
if (*T)
{
if ((*T)->lchild) /* 有左孩子 */
DestroyBiTree(&(*T)->lchild); /* 销毁左孩子子树 */
if ((*T)->rchild) /* 有右孩子 */
DestroyBiTree(&(*T)->rchild); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
*T = NULL; /* 空指针赋0 */
}
}
6.7.11 打印二叉树:
顺序存储:
Status visit(TElemType c)
{
printf("%d ", c);
return OK;
}
/* 层序遍历二叉树 */
void LevelOrderTraverse(SqBiTree T)
{
int i = MAX_TREE_SIZE - 1, j;
while (T[i] == Nil)
i--; /* 找到最后一个非空结点的序号 */
for (j = 0; j <= i; j++) /* 从根结点起,按层序遍历二叉树 */
if (T[j] != Nil)
visit(T[j]); /* 只遍历非空的结点 */
printf("\n");
}
/* 逐层、按本层序号输出二叉树 */
void Print(SqBiTree T)
{
int j, k;
Position p;
TElemType e;
for (j = 1; j <= BiTreeDepth(T); j++)
{
printf("第%d层: ", j);
for (k = 1; k <= powl(2, j - 1); k++)
{
p.level = j;
p.order = k;
e = Value(T, p);
if (e != Nil)
printf("%d:%d ", k, e);
}
printf("\n");
}
}
6.8 线索二叉树:
链式二叉树的建立,会留下许许多多空指针的存在,如果放着不用,是比较浪费的。所以我们可以利用空地址,存放指向结点在某种遍历次序下的前驱和后继结点的地址。
我们把这种指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。(Threaded Binary Tree)。
我们通过设置ltag和rtag变量,用来存放0、1数字的布尔型变量。ltag为0时指向左孩子,1时指向前驱。rtag为0时指向右孩子,1时指向后继。
如图所示:
6.8.1 结构体:
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char TElemType;
typedef enum { Link, Thread } PointerTag; /* Link==0表示指向左右孩子指针, */
/* Thread==1表示指向前驱或后继的线索 */
typedef struct BiThrNode /* 二叉线索存储结点结构 */
{
TElemType data; /* 结点数据 */
struct BiThrNode* lchild, * rchild; /* 左右孩子指针 */
PointerTag LTag;
PointerTag RTag; /* 左右标志 */
} BiThrNode, * BiThrTree;
TElemType Nil = '#'; /* 字符型以空格符为空 */
6.8.2 前序构造线索二叉树:
/* 按前序输入二叉线索树中结点的值,构造二叉线索树T */
/* 0(整型)/空格(字符型)表示空结点 */
Status CreateBiThrTree(BiThrTree* T)
{
TElemType h;
scanf("%c", &h);
if (h == Nil)
*T = NULL;
else
{
*T = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*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;
}
6.8.3 中序遍历以及线索化:
BiThrTree pre; /* 全局变量,始终指向刚刚访问过的结点 */
/* 中序遍历进行中序线索化 */
void InThreading(BiThrTree p)
{
if (p)
{
InThreading(p->lchild); /* 递归左子树线索化 */
if (!p->lchild) /* 没有左孩子 */
{
p->LTag = Thread; /* 前驱线索 */
p->lchild = pre; /* 左孩子指针指向前驱 */
}
if (!pre->rchild) /* 前驱没有右孩子 */
{
pre->RTag = Thread; /* 后继线索 */
pre->rchild = p; /* 前驱右孩子指针指向后继(当前结点p) */
}
pre = p; /* 保持pre指向p的前驱 */
InThreading(p->rchild); /* 递归右子树线索化 */
}
}
/* 中序遍历二叉树T,并将其中序线索化,Thrt指向头结点 */
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); /* 中序遍历进行中序线索化 */
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;
while (p->RTag == Thread && p->rchild != T)
{
p = p->rchild;
visit(p->data); /* 访问后继结点 */
}
p = p->rchild;
}
return OK;
}
如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择。
6.9 树、森林、二叉树转换:
6.9.1 树转化为二叉树:
- 加线,在所有兄弟结点之间加一条连线。
- 去线,对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线。
- 层次调整。以树的根结点为核心,将整棵树顺时针旋转一定的角度,是指结构层次分明。注意:第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结点的右孩子。
6.9.2 森林转化为二叉树:
- 把每个树转换为二叉树
- 第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根节点的右孩子,用线连接起来。当所有的二叉树连接起来后就得到了由森林转化来的二叉树。
6.9.3 二叉树转换为树:
- 加线。若某结点的左孩子结点存在,则将这个左孩子的n个右孩子结点都作为此结点的孩子。将该结点与这些右孩子结点用线连接起来。
- 去线。删除原二叉树中所有结点与其右孩子的连线。
- 层次调整。使之结构层次分明。
6.9.4 二叉树转换为森林:
- 从根结点开始,若右孩子存在,则把与右孩子结点的连线删除,再查看分离后的二叉树,若右孩子存在,则连线删除…,直到所有右孩子连线都删除为止,得到分离的二叉树。
- 再将每棵分离后的二叉树转换为树即可。
本文为自己在学习数据结构过程中所记录整理的一些笔记,后续将持续更新后面的内容。
如果这篇文章对你有帮助,可以给我点一个小小的赞。
如果你对我的栏目有兴趣,也欢迎关注我,一同学习。
主要参考:《大话数据结构》——程杰
往期回顾:
【数据结构】串 学习笔记
【数据结构】队列 学习笔记
【数据结构】栈 学习笔记
【数据结构】线性表 学习笔记