线索二叉树与森林
线索二叉树
定义
n个结点的二叉链表中含有n+1个空指针域为什么,详见:数据结构复习之树与二叉树中的二叉链的部分。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前趋和后继结点的指针(这种附加的指针称为"线索")。
这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
结点结构
lchild | itag | data | rtag | rchild |
---|
说明:
ltag=0,lchild域指向结点的左孩子
=1,lchild域指向结点的前趋
rtag=0,rchild域指向结点的右孩子
=1,rchild域指向结点的后继
线索链表定义
typedef struct node
{
DataType data;
int ltag, rtag;
struct node *lchild, *rchild;
} BinThrNode;//线索链表结点类型
typedef BinThrNode *BinThrTree;
//定义线索链表类型
二叉树线索化算法
思想
按某种次序遍历二叉树,在遍历过程中用线索取代空指针即可.
二叉链表加中序线索算法
算法与中序遍历算法类似,只是将遍历算法中访问根结点*bt的操作改为在*bt和中序前趋*pre之间建立线索。
void InorderThread(BinThrTree bt)
{
Static BinThrNode *pre = NULL; //定义指向前趋结点的指针pre(静态变量,只初始化1次),保存刚访问过的结点
if (bt != NULL) //当二叉树为空时结束递归
{
InorderThread(bt->lchild); //左子树线索化
if (bt->lchild == NULL)
bt->ltag = 1;
else
bt->ltag = 0;
if (bt->rchild == NULL)
bt->rtag = 1;
else
bt->rtag = 0;
if (pre)
{
if (pre->rtag == 1)
pre->rchild = bt; //给前趋结点加后继线索
if (bt->ltag == 1)
bt->lchild = pre; //给当前结点加前趋线索
}
pre = bt; //将刚访问过的当前结点置为前趋结点
InorderThread(bt->rchild); //右子树线索化
}
}
线索链表上运算
在中序线索二叉树上查找某结点*p的后继结点
分析:
(1)若结点*p的rtag域值为1,则表明p->rchild为右线索,它直接指向结点。
(2)若结点*p的rtag域值为0,则表明p->rchild指向右孩子结点,结点*p的中序后继结点必是其右子树第一个中序遍历到的结点,因此从结点*p的右孩子开始,沿左指针链向下查找,直到找到一个没有左孩子(即ltag为1)的结点为止,该结点是结点*p的右子树中"最左下"的结点,它就是结点*p的中序后继结点。
算法实现
BinThrNode *InorderNext(BinThrNode *p)
{ //在中序线索二叉树上求结点*p的中序后继结点
if (p->rtag == 1) // rchild域为右线索
return p->rchild; //返回中序后继结点指针
else {
p = p->rchild; //从*p的右孩子开始
while (p->ltag == 0) p = p->lchild; //沿左指针链向下查找
return p;
}
}
线索二叉树中序遍历
分析:
首先从根结点起沿左指针链向下查找,直到找到一个左线索标志为1的结点止,该结点的左指针域必为空,它就是整个中序序列中的第一个结点。访问该结点,然后就可以依次找结点的后继,直至中序后继为空时为止。
void TinorderThrTree(BinThrThrtree bt)
{
BinThrNode *p;
if (bt != NULL) //二叉树不空
{
p = bt; //使p指向根结点
while (p->ltag == 0) //查找出中序遍历的第一个结点
p = p->lchild;
do {
printf("%c", p->data); //输出访问结点值
p = InorderNext(p); //调用前面的函数查找结点*p的中序后继
} while (p != NULL); //当p为空时算法结束
}
}
森林和树
树的存储结构
双亲表示法
如下图所示:
双亲域,存储双亲结点数组中的下标值。找儿子不太方便。
孩子链表表示法
孩子链表是一个带头结点的单链表。
为了便于查找双亲,可在各个头结点中增加一个双亲域以存储双亲结点在头结点数组中的下标值,称其为带双亲的孩子链表表示法。
带双亲孩子链表表示法
孩子兄弟表示法
数据域–存储树上结点的数据元素;孩子域–存放指向本结点第一个孩子的指针;兄弟域–存放指向本结点下一个兄弟的指针。
如下图:
树,森林与二叉树转换 考点
二叉树有比较成熟的算法。
树转换成二叉树
步骤:
根转化完了一定没有右子树,因为是原先兄弟结点转化成右子树,根没有兄弟。
① 在所有相邻兄弟结点之间分别加一条连线。
② 对每个分支结点,除了其最左孩子外,删去该结点与其他孩子结点的连线。
③ 以根结点为轴心,顺时针旋转45°
如下图所示:
森林转换成二叉树
步骤:
① 分别将树林中的每棵树转换为二叉树。
② 从最后那棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,直到所有二叉树全部连接,这样得到的二叉树的根结点就是树林中第一棵树的根结点。
如下图所示:
二叉树转换为树
目的是利用二叉树计算完成后,得还原回去。
(是否存在左孩子,存在再一路找右孩子的右孩子,右孩子变成兄弟)
步骤:
① 若某结点是其双亲结点的左孩子,则将该结点的右孩子以及当且仅当连续地沿着此右孩子的右子树方向不断地搜索到的所有右孩子,都分别与该结点的双亲结点用虚线连接起来。
② 删去原二叉树中所有双亲结点与其右孩子的连线。
③ 将图形规整化,使各结点按层次排列,并且将虚线改成实线。
如下图所示:
二叉树还原为森林
① 去掉二叉树的右子树,将去掉右子树后剩下的二叉树转换为一棵树。
② 将在第①步中被去掉的右子树再执行第①步。
③ 重复前两步,直到转换完成。
如下图所示:
树与森林的遍历
树的遍历
树的前序遍历定义:
①访问根结点; ②依次前序遍历根的各子树。
树的后序遍历定义:
①依次后序遍历根的各子树; ②访问根结点R。
注意:
① 前序遍历一棵树恰好等价于前序遍历该树对应的二叉树;
② 后序遍历一棵树恰好等价于中序遍历该树对应的二叉树
森林遍历
前序遍历
①访问森林中第一棵树的根结点;
②前序遍历第一棵树中根结点的各子树所构成的森林
③前序遍历除第一棵树外其它树构成的森林。
后序遍历
①后序遍历森林中第一棵树的根结点的各子树所构成的森林;
②访问第一棵树的根结点;
③后序遍历除第一棵树外其它树构成的森林。
注意:
① 前序遍历森林等同于前序遍历该森林对应的二叉树
② 后序遍历森林等同于中序遍历该森林对应的二叉树