这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战
1.线索二叉树
因为普通的二叉树 不能直接找到当前结点的前驱和后继 就出现了线索二叉树。
- 二叉树的线索化:若当前结点没有左子树,则将左指针指向前驱结点;若无右子树,则将右指针指向后继结点。
- 二叉树的存储结构
相比与普通的二叉树,线索二叉树多了两个标志,ltag,rtag.这两个标志主要用来干啥呢?
当ltag/rtag 为0 表示 该结点有左/右孩子即lchild/rchild指向左/右孩子
当ltag/rtag 为1 表示 该结点无左/右孩子即lchild/rchild指向前驱结点/后继结点
其存储结构:
typedef struct ThreadNode{
char data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
2. 相关算法
1 中序线索化
//z中序线索化
void InitThread (ThreadTree T,ThreadTree &pre){
if(p != NULL){
InitThread(T->lchild,pre); //1.最先执行的结点时B结点
if(p->lchild == null){
p -> lchild = pre; // 2.此时p的做线索为NULL
p -> ltag = 1;
}
if(pre != NUll && pre->rchild == NULL){ //3.pre目前为NULL 不进入
pre -> rchild = p;
pre -> rtag = 1;
}
pre = p; //pre用来记录p的前驱结点 是一个全局变量 //4.此时将Pre指向B结点
InitThread(T->rchild,pre);
}
}
//线索化二叉树
void createThreadTree(ThreadTree T){
ThreadTree pre = NULL;
if(T != NULL){
InitThread(T,pre);
pre->rchild = NULL;
pre->rtag = 1;
}
}
关于中序线索化的算法 如果不好理解可以结合下面这张图(以及我在代码中标注的1~4的序号)来理解
先序和后续线索化和上面的内容基本一样 只是递归方式的顺序发生了变化
2 找指定二叉树指定次序上的前驱和后继
1. 中序线索二叉树:
-
查找P的前驱,查ltag为0 则遍历左子树访问其最后一个结点,若ltag为1 则可以直接查找左子树的结点。
-
查找P的后继结点,查rtag为0,后继则是遍历右子树的第一个结点
中序遍历找前驱
//遍历左子树访问其最后一个结点
ThreadNode *lastNode(ThreadNode *p){
while(p->rtag == 0){
//根据中序:左根右 循环找到左子树最右下的结点就为左子树最后一个结点
p = p->rchild;
}
return p;
}
//找前驱
ThreadNode *findPre(ThreadNode *p){
if(ltag == 0) {
return lastNode(p->lchild);
}else{
return p->lchild;
}
}
中序遍历找前驱
//遍历左子树访问其最后一个结点
ThreadNode *firstNode(ThreadNode *p){
while(p->ltag == 0){
//根据中序:左根右 循环找到右子树最左下的结点就为右子树第一个结点
p = p->lchild;
}
return p;
}
//找前驱
ThreadNode *findPre(ThreadNode *p){
if(rtag == 0) {
return firstNode(p->rchild);
}else{
return p->rchild;
}
}
2. 先序线索二叉树:(根左右)
查找P的前驱,查ltag为0,则前驱结点则是结点的双亲结点。 查找P的后继结点,查rtag为0,其后继为:结点左子树存在 则为左子树的根结点,不存在则为右子树的根结点
3. 后续线索二叉树:(左右根)
查找p的前驱,查ltag为0且rtag为0,则p的前驱是右子树的根结点,查ltag为0但rtag为1 则p的前驱是左子树的根结点,
查p的后继
a.若p为根结点 则后继为null;
b. p为右子树的根结点,后继为双亲结点;
c. p为左子树的根结点 若无右兄弟 则后继为双亲,若有右兄弟,则后继则为后续遍历双亲结点右子树时访问的第一个结点。
后续线索二叉树 理解可以结合这张图来理解