线索二叉树
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JD *zxxsh(JD *bt) A { JD *p,*pr,*s[M],*t; • 算法 int i=0; – 按中序线索化二叉树 B D t=(JD *)malloc(sizeof(JD)); t->lt=0; t->rt=1; t->rc=t; if(bt==NULL) t->lc=t; i C E Ch5_20.txt else P->C { t->lc=bt; pr=t; p=bt; P->A do{ while(p!=NULL) { s[i++]=p; p=p->lc; } t if(i>0) { p=s[--i]; 0 1 printf("%c ",p->data); bt if(p->lc==NULL) { p->lt=1; p->lc=pr;} 0 A 0 pr if(pr->rc==NULL) { pr->rt=1; pr->rc=p;} pr=p; p=p->rc; 1 B 0 0 D 0 ^ } }while(i>0||p!=NULL); P pr->rc=t; pr->rt=1; t->rc=pr; } ^ 0 C 0 ^ ^ 0 E 0 ^ return(t); Ch5_20.c 输出:B }
中序线索二叉树BDAEC 中序线索二叉树
A B D G J L
先序线索二叉树 ABDEGJCFHKLI
A C B F H K I D G J L
后序线索二叉树 DJGEBLKHIFCA
C E H K F I
E
• 中序线索二叉树 • 后继:结点标志为1时,其右指针为其后 继;结点标志为0时,其右子树最左下结 点为其后继。 • 前驱:结点标志为1时,其左指针为其前 驱;结点标志为0时,其左子树最右下结 点为其前驱。
6.3.2线索二叉树: • 1、线索化的概念 当以二叉链表作为存储结构时,只能找到结点的左右孩 子的信息,而不能在结点的任一序列的前驱与后继信 息。 遍历二叉树实为把非线性结构线性化。而这种线性有 序的信息是否可以在存储结构中保持住呢?即保持 其前驱和后继关系。线索化二叉树为我们提供了肯 定的答案。 线索树是二叉树的一种存储方式,是二叉树以某种方 式遍历所获得序列在物理上的记录。有了线索化的 二叉树,除了可进行全面的各种遍历外,还可以方 便地进行求直接后继、直接前驱、插入一个新结点 以及删除一个指定结点等运算。
• 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;}// 建立后继线索 • pre=p;//保持pre为下一结点的前驱 • InThreading(p->rchild); //右子树线索化 • } • }//InThreading
• 在线索链表中添加了一个“头结点”,头结点的左指针指向 二叉树的根结点,其右线索指向中序序列中的最后一个结点。 这好比为二叉树建立了一个双向线索链表,既可从第一个结 点起顺后继进行遍历,也可从最后一个结点起顺前驱进行遍 历。
Status InOrderTraverse_Thr(BiThrTree T, Status (*Visit)(TElemType e)){ p=T->lchild; while(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; } }//InOrderTraverse_Thr
寻找当前结点 在中序下的前驱
A B D G J L E H K C F
if (current→LTag ==Thread) 前驱为current→lchild 前驱为 else //current→LTag ==Link 前驱为当前结点左子树的 中序下的最后一个结点
I
中序前驱线索二叉树 中序序列:DBGJEACHLKFI
• 后序线索二叉树 • 后继:(1)根的后继为空。 • (2)如果结点是双亲的右孩子或 是左孩子但双亲没有右子树,则后继是 其双亲。 • (3)如果结点是双亲的左孩子且 双亲有右子树,则后继是双亲右子树上 先遍历到的结点。 • 总之,如果二叉树的应用主要是遍历或 查找结点的前驱和后继则使用线索二叉 树更为合适。
A B i P->A pr t 0 1 bt p 0 A 0 C E D
^ 0 B0 ^
^ 0 E 0 ^
JD *zxxsh(JD *bt) { JD *p,*pr,*s[M],*t; • 算法 int i=0; – 按中序线索化二叉树 t=(JD *)malloc(sizeof(JD)); t->lt=0; t->rt=1; t->rc=t; if(bt==NULL) t->lc=t; Ch5_20.txt else { t->lc=bt; pr=t; p=bt; do{ while(p!=NULL) { s[i++]=p; p=p->lc; } if(i>0) { p=s[--i]; printf("%c ",p->data); if(p->lc==NULL) { p->lt=1; p->lc=pr;} if(pr->rc==NULL) { pr->rt=1; pr->rc=p;} pr=p; p=p->rc; } }while(i>0||p!=NULL); pr->rc=t; pr->rt=1; t->rc=pr; } return(t); Ch5_20.c }
JD *zxxsh(JD *bt) { JD *p,*pr,*s[M],*t; • 算法 int i=0; – 按中序线索化二叉树 t=(JD *)malloc(sizeof(JD)); t->lt=0; t->rt=1; t->rc=t; if(bt==NULL) t->lc=t; Ch5_20.txt else { t->lc=bt; pr=t; p=bt; do{ while(p!=NULL) { s[i++]=p; p=p->lc; } if(i>0) { p=s[--i]; printf("%c ",p->data); if(p->lc==NULL) { p->lt=1; p->lc=pr;} if(pr->rc==NULL) { pr->rt=1; pr->rc=p;} pr=p; p=p->rc; } }while(i>0||p!=NULL); pr->rc=t; pr->rt=1; t->rc=pr; } return(t); Ch5_20.c }
试作如下规定: 若结点有左子树,则其lchild域指示其左孩子,否 则令lchild域指示前驱;若结点有右子树,则其 rchild指示其右孩子,否则令rchild指示其后继。 为了避免混淆,尚需改变结点的结构,增加标志域; lchild ltag data rtag rchild 其中: 0 lchild 域指示结点的左孩子 ltag={ 1 lchild 域指示结点的前驱 0 rchild 域指示结点的右孩子 rtag={ 1 rchild 域指示结点的后驱 以这种结构构成的二叉链表作为二叉树的存储 结构,叫做线索链表 线索链表,其中指向结点前驱与后继的指 线索链表 针叫做线索 线索.加上线索的二叉树称之为线索二叉树 线索 线索二叉树
通过中序遍历建立中序线索化二叉树
• Status InOrderThreading(BiThrTree &Thrt, BiThrTree T){ • //中序遍历并线索化二叉树,Thrt为头结点 • if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode)))) exit(OVERFLOW); //建头结点 • Thrt->LTag=Link; • Thrt->RTag=Thread; • Thrt->rchild=Thrt; //头结点右指针回指 • if(!T)Thrt->lchild=Thrt; //如果二叉树为空,则左指针也回指 • else{ • Thrt->lchild=T; prt=Thrt;//pre总是指向最后一个访问 过的结点,也就是其后要访问结点的前驱 • InThreading(T);//线索化的主过程 • pre->rchild=Thrt; pre->RTag=Thread; • Thrt->rchild=pre;//将最后一个结点线索化 • } • return OK; • }//InOrderThreading
A i P->B P->A pr t 0 bt 0 A 0 p ^ 0 B 0 0 D0 ^ 1 B C E D
^ 0 C 0 ^
^ 0 E 0 ^
A JD *zxxsh(JD *bt) { JD *p,*pr,*s[M],*t; • 算法 int i=0; B D – 按中序线索化二叉树 t=(JD *)malloc(sizeof(JD)); t->lt=0; t->rt=1; t->rc=t; i C E if(bt==NULL) t->lc=t; Ch5_20.txt P->B else { t->lc=bt; pr=t; p=bt; P->A do{ while(p!=NULL) { s[i++]=p; p=p->lc; } pr if(i>0) t { p=s[--i]; 0 1 printf("%c ",p->data); bt if(p->lc==NULL) { p->lt=1; p->lc=pr;} 0A 0 if(pr->rc==NULL) { pr->rt=1; pr->rc=p;} pr=p; p=p->rc; } ^ 0 B 0 0 D 0 ^ }while(i>0||p!=NULL); P=NULL pr->rc=t; pr->rt=1; t->rc=pr; } ^ 0 C 0 ^ ^ 0 E 0 ^ return(t); Ch5_20.c }
JD *zxxsh(JD *bt) A { JD *p,*pr,*s[M],*t; • 算法 int i=0; – 按中序线索化二叉树 B D t=(JD *)malloc(sizeof(JD)); t->lt=0; t->rt=1; t->rc=t; if(bt==NULL) t->lc=t; C E Ch5_20.txt i else { t->lc=bt; pr=t; p=bt; P->A do{ while(p!=NULL) pr { s[i++]=p; p=p->lc; } t if(i>0) 0 1 { p=s[--i]; bt printf("%c ",p->data); if(p->lc==NULL) { p->lt=1; p->lc=pr;} 0 A0 if(pr->rc==NULL) P { pr->rt=1; pr->rc=p;} pr=p; p=p->rc; ^ 0 B0 1 0 D0 ^ } }while(i>0||p!=NULL); pr->rc=t; pr->rt=1; t->rc=pr; } ^ 0 C 0 ^ ^ 0 E 0 ^ return(t); Ch5_20.c 输出:B }
寻找当前结点 if (current→RTag ==Thread) 在中序下的后继 后继为 current→rchild else //current→RTag ==Link A 后继为当前结点右子树 的中序下的第一个结点 B C
D G J L E H K F I
中序后继线索二叉树 DBGJEACHLKFI
typedef enum {Link,Thread} PointerTag; //PointerTag为标志,Link=0代表指针, 为标志, 代表指针, 为标志 代表指针 Thread=1代表线索 代表线索 typedef struct BiThrNode{ TElemType data; struct BiThrNode *lchild, *rchild; PointerTag LTag,RTag; }BiThrNode, *BiThrTree;
• 如何保存这种在遍历过程中得到的信息? 两个办法—— • 法1 : 在每个结点上增加两个指针域fwd和bkwd,分别指示结 点在任一次序遍历时得到的前驱和后继信息。(存 储密度大大降低) • 法2: 在n个结点的二叉链表中必定存在n+1个空链域。由此 空链域。 空链域 设想能否利用这些空链域来存放结点的前驱和后继 的信息。