在介绍线索二叉树之前,我们需要知道对于一个有n个结点的二叉树,每个结点有指向左右孩子的两个指针域,所有一共有2n个指针域。而n个结点一共有n-1个分支线数,则存在2n-(n-1)=n+1个空指针域,而这里介绍的都是指中序线索二叉树。
对于任意的遍历二叉树,为了解决清楚地知道任意一个结点的前驱和后继的问题,引入了线索二叉树,把指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树,对应的数据结构是
typedef enum{Link,Thread}PointType;
struct BiTree{
struct BiTree *lchild,*rchild
PointType ltag,rtag;
char data;
};
而线索二叉树就是有效的利用前面计算中存在的n+1个空指针域,让所有的空指针域中的rchild指向它的后继结点,让所有空指针域的lchild指向它的前驱结点。如图,中序的遍历是HDIBJEAFCG
其实线索二叉树就是把一颗二叉树转变 成了双向链表。一开始还觉得这个观点很新颖,但是想想确实是那么回事,如图(中序遍历HDIBJEAFCG)
完成线索二叉树和遍历的函数一共有四个,其中1.创建二叉树 2.线索化二叉树 3.初始化线索二叉树 4.遍历线索二叉树,首先是1,和上一节介绍的一样,只不过对于有左子树和右子树的结点的ltag和rtag都应该为Link(默认0),代码如下:
struct BiTree *createTree(){
struct BiTree *root;
char val;
scanf("%c",&val);
if(val=='#') return NULL;
else{
root=(struct BiTree*)malloc(sizeof(struct BiTree));
if(!root) return NULL;
root->data=val;
root->lchild=createTree();
if(root->lchild) root->ltag=Link;
root->rchild=createTree();
if(root->rchild) root->rtag=Thread;
}
return root;
}
核心的函数2,线索二叉树函数,就是对于每一个结点如果左子树为空,就让ltag=Thread,然后让ltag指向前驱结点,如果右子树为空,就让rtag=Thread,然后让rtag指向后继结点,既然存在前驱结点,那么肯定需要一个指针指向前驱,在代码中就是prestruct BiTree *pre;
void inThreading(struct BiTree* root){
if(root){
inThreading(root->lchild);
if(!root->lchild){
root->ltag=Thread;
root->lchild=pre;
}
if(!pre->rchild){
pre->rtag=Thread;
pre->rchild=root;
}
pre=root;
inThreading(root->rchild);
}
}
看上面的代码,其实去除两个if和pre的赋值,不就是中序遍历的代码嘛,只不过现在加上了对于左右标志的赋值,和对于前驱后继指针的修改。对于步骤3,由于2中用到了pre指针,但是明显没有初始化,而线索二叉树和双向链表一样,加上了一个头结点,让其lchild指向二叉树的根节点,其rchild指向二叉树的中序遍历的最后一个节点,可以参考第一个图片,pre的初始值是指向头结点,然后每次都指向中序序列的下一个结点(HDIBJEAFCG)。接下来就是初始化函数:
int initThreadTree(struct BiTree**head,struct BiTree *root){
*head=(struct BiTree*)malloc(sizeof(struct BiTree));
if(!*head) return 0;
(*head)->ltag=Link;
(*head)->rtag=Thread;
(*head)->rchild=*head;
if(!root){
(*head)->lchild=*head;
}
else{
(*head)->lchild=root;
pre=(*head); //初始化pre全局变量
threading(root);
pre->rtag=Thread;
pre->rchild=*head;
(*head)->rchild=pre;
}
return 1;
}
其实步骤4,有2和3这两个函数就已经完成了对于二叉树的线索化的过程,但是最后我们怎么知道自己写的对不对呢,这样就多了一个遍历的函数,代码如下:
int threadTravderse(struct BiTree *head){
struct BiTree *p=head->lchild;
while(p!=head){
while(p->ltag==Link){
p=p->lchild;
}
printf("%c",p->data);
while(p->rtag==Thread&&p->rchild!=head){ //p肯定是指向到最后节点,所以p->rchild最后指向head
p=p->rchild;
printf("%c",p->data);
}
p=p->rchild;
}
return 1;
}
总的代码如下:
#include <stdio.h>
#include <stdlib.h>
typedef enum{Link,Thread}PointType;
struct BiTree{
struct BiTree *lchild,*rchild;
PointType ltag,rtag;
char data;
};
struct BiTree *pre;
struct BiTree* createTree(){
char val;
struct BiTree *root;
scanf("%c",&val);
if(val=='#'){
return NULL;
}
else{
root=(struct BiTree*)malloc(sizeof(struct BiTree));
if(!root) return NULL;
root->data=val;
root->lchild=createTree();
if(root->lchild) root->ltag=Link; //左子树不为空,则ltag=0
root->rchild=createTree();
if(root->rchild) root->rtag=Link;
}
return root;
}
int threading(struct BiTree *root){
if(root){
threading(root->lchild);
if(!root->lchild){ //左孩子为空
root->ltag=Thread;
root->lchild=pre;
}
if(!pre->rchild){ //右孩子为空
pre->rtag=Thread;
pre->rchild=root;
}
pre=root;
threading(root->rchild);
}
return 1;
}
int initThreadTree(struct BiTree**head,struct BiTree *root){
*head=(struct BiTree*)malloc(sizeof(struct BiTree));
if(!*head) return 0;
(*head)->ltag=Link;
(*head)->rtag=Thread;
(*head)->rchild=*head;
if(!root){
(*head)->lchild=*head;
}
else{
(*head)->lchild=root;
pre=(*head); //初始化pre全局变量
threading(root);
pre->rtag=Thread;
pre->rchild=*head;
(*head)->rchild=pre;
}
return 1;
}
int threadTravderse(struct BiTree *head){
struct BiTree *p=head->lchild;
while(p!=head){
while(p->ltag==Link){
p=p->lchild;
}
printf("%c",p->data);
while(p->rtag==Thread&&p->rchild!=head){ //p肯定是指向到最后节点,所以p->rchild最后指向head
p=p->rchild;
printf("%c",p->data);
}
p=p->rchild;
}
return 1;
}
int main(){
struct BiTree *head=NULL,*root=NULL;
printf("先序创建二叉树(如ABDH##I##EJ###CF##G##)\n");
root=createTree();
initThreadTree(&head,root);
threadTravderse(head);
return 0;
}
其中初始化二叉树的head是一个二级指针,这和我在双向链表中需要解决的问题的方式一样,不赘述。我也是看完大话之后,自己理解的情况下再敲的一遍,其实文中反复提到中序遍历的结果,写代码的时候,对照第一张图,然后想清楚现在到了哪一个结点,再思考这样写感觉就八九不离十了。
未完待续 下一节 二叉树算法三之哈弗曼树和编码
如果文章有什么错误或者有什么建议,欢迎提出,大家共同交流,一起进步
文章转载请注明出处,请尊重知识产权