提示:下面案例可供参考
一、线索二叉树
遍历二叉树是以一定的规则将二叉树中的结点排列成一个线性序列。从而得到几种遍历序列,使得该序列中的每个结点(第一个和最后一 个结点除外) 都有一个直接前驱和直接后继。
传统的二叉链表存储仅能体现一种父子关系,不能直接得到结点在遍历中的前驱或后继。在含n个结点的二叉树中,有n+1个空指针。这是因为每个叶结点有2个空指针,每个度为1的结点有1个空指针,空指针总数为2n0+n1, 又no=n2+1,所以空指针总数为n0+n1+n2+1=n+1。由此设想能否利用这些空指针来存放指向其前驱或后继的指针?
这样就可以像遍历单链表那样方便地遍历二叉树。所以引入线索叉树正是为了 加快查找结点前驱和后继的速度。规定:若无左子树,令lchild指向其前驱结点;若无右子树,令rchild指向其后继结点。
二、线索二叉树的实现
1.存储结构和一些必要的函数基础
线索二叉树的存储结构(这里用的编译环境是devc++)
#include<bits/stdc++.h> using namespace std; typedef struct ThreadTNode{ // 线索二叉树 struct char data; int ltag; //0 表示指向的是 左 右 结点 1 表示指向的是 前驱 后继 int rtag; struct ThreadTNode * lchild; struct ThreadTNode * rchild; }ThreadNode,*ThreadTree;
ThreadTree prebuild(){ //先序建立二叉树 (不是线索) char s; scanf("%c",&s); if(s!='#'){ ThreadTNode *p=(ThreadTNode *)malloc(sizeof(ThreadNode)); //c++用new p->data=s; p->lchild=prebuild(); p->rchild=prebuild(); p->ltag=0;p->rtag=0; return p; } return NULL; }
void PreOrder(ThreadTree t){ //先序遍历 if(t!=NULL){ printf("%c",t->data); if(t->ltag==0) PreOrder(t->lchild); if(t->rtag==0) PreOrder(t->rchild); } } void LevelOrder(ThreadTree t){ //层序遍历 queue<ThreadTree> q; q.push(t); ThreadTNode *s; while(!q.empty()){ s=q.front(); if(s->ltag==0&&s->lchild!=NULL) q.push(s->lchild); if(s->rtag==0&&s->rchild!=NULL) q.push(s->rchild); cout<<s->data<<s->ltag<<s->rtag<<" "; //用于查看线索二叉树 q.pop(); } printf("\n"); } void deletetree(ThreadTree t){ //后序的思想 销毁 树 if(!t){ if(t->ltag==0) deletetree(t->lchild); if(t->rtag==0) deletetree(t->rchild); free(t); } }
创建一个二叉树 :
int main(){
ThreadTree T; T=prebuild();
PreOrder(T);printf("\n");
LevelOrder(T);
deletetree(T);
return 0;
}
输入:ABD##E##CF###
输出:
2.线索二叉树的实现
先序线索二叉树的实现:
/先序 线索化
void PreThread(ThreadTree t,ThreadTree &pre) {
if(t!=NULL){
if(t->lchild==NULL){ //前驱
t->lchild=pre;
t->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=t; //后继
pre->rtag=1;
}
pre=t;
if(t->ltag==0) PreThread(t->lchild,pre);
if(t->rtag==0) PreThread(t->rchild,pre);
}
}
void CreatePreThread(ThreadTree t){
ThreadTree pre=NULL;//指针
if(t!=NULL){
PreThread(t,pre);
pre->rchild=NULL; //处理最后一个结点
pre->rtag=1;
}
cout<<"先序线索化完成--------\n";
}
中序线索二叉树的实现:
//中序 线索化
void InThread(ThreadTree t,ThreadTree &pre) {
if(t!=NULL){
InThread(t->lchild,pre);
if(t->lchild==NULL){ //前驱
t->lchild=pre;
t->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=t; //后继
pre->rtag=1;
}
pre=t;
InThread(t->rchild,pre);
}
}
void CreateInThread(ThreadTree t){
ThreadTree pre=NULL;//指针
if(t!=NULL){
InThread(t,pre);
pre->rchild=NULL;
pre->rtag=1;
}
cout<<"中序线索化完成--------\n";
}
后序线索二叉树的实现
/后序 线索化
void PostThread(ThreadTree t,ThreadTree &pre) {
if(t!=NULL){
if(t->ltag==0) //这里的if是没必要的 只是写了强调一下
PostThread(t->lchild,pre);
if(t->rtag==0) PostThread(t->rchild,pre);
if(t->lchild==NULL){ //前驱
t->lchild=pre;
t->ltag=1; }
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=t; //后继
pre->rtag=1; }
pre=t;
}
}
void CreatePostThread(ThreadTree t){
ThreadTree pre=NULL;//指针
if(t!=NULL){
PostThread(t,pre);
//最后的是指向根节点 根节点没有空余的指针
}
cout<<"后序线索化完成--------\n";
}
C语言不支持参数& 所以用常量代替。后序线索为例:
ThreadTree pre2=NULL;
void PostThread2(ThreadTree t) {
if(t!=NULL){
PostThread2(t->lchild);
PostThread2(t->rchild);
if(t->lchild==NULL){ //前驱
t->lchild=pre2;
t->ltag=1;
}
if(pre2!=NULL&&pre2->rchild==NULL){
pre2->rchild=t; //后继
pre2->rtag=1;
}
pre2=t;
}
}
void CreatePostThread2(ThreadTree t){
if(t!=NULL){
PostThread2(t);
}
printf("后序线索化2完成--------\n")
}
3.中序线索化为例深入讨论前驱后继
线索化完成,下一步的任务就是查找前驱后继。
以下面的二叉树为例子:ABD##E##CF###
中序线索化后:
有蓝色箭头的结点很明显就能判断出它的前驱和后继,也就是左指针与右指针。
没有蓝色箭头的呢?
对于A结点来说它的中序前驱,就是左子树的最右下的结点,也就是E。
对于A结点来说它的中序后继,就是右子树的最左下的结点,也就是F。同样适用于其他的结点
有了这样的思想,就可以写出代码
ThreadNode *Firstnode(ThreadNode *p){ //第一个结点
while(p->ltag==0) p=p->lchild; //该结点 最左下
return p;
}
ThreadNode *Nextnode(ThreadNode *p){
if(p->rtag==1) return p->rchild;
else return Firstnode(p->rchild); //右孩子 最左下的
}
ThreadNode *Priornode(ThreadNode *p){
if(p->ltag==1) return p->lchild;
else{
p=p->lchild; //左孩子 最右边的是 前驱
while(p->rtag==0) p=p->rchild;
return p; }
}
void Inorder(ThreadNode *t){
for(ThreadNode *p=Firstnode(t);p!=NULL;p=Nextnode(p)){
printf("%c",p->data);
}printf("\n");
}
主函数调试
int main(){
ThreadTree T;
T=prebuild();
//中序线索化
CreateInThread(T);
LevelOrder(T); //层序查看
cout<<T->data<<"的后继是"<<Nextnode(T)->data<<endl;
cout<<T->data<<"的前驱是"<<Priornode(T)->data<<endl;
cout<<"中序线索遍历:";
Inorder(T); //遍历
deletetree(T);
return 0;
}
输入:ABD##E##CF###
输出:
先序和后序也是类似的。
总结
正在学习的程序⚪ 。以上的内容就是数据结构线索二叉树的基本内容。如果有什么错误或问题下方留言。