后序线索二叉树的构造
三叉链表结构
结构体要用三叉链表,因为在遍历中序线索二叉树的时候需要找到某个节点的后继结点,对于右孩子来讲,其后继结点即为它的双亲,所以需要找到其双亲结点,故要用三叉链表
bool CreateThreadTree(ThreadTree &T, ThreadTree parent)树的创建需要多加一个parent参数,对于根节点的parent置为NULL, 在创建树的时候就传入NULL作为参数
后序线索二叉树和中序,前序有所不同,需要特别注意
typedef struct ThreadNode{
ElemType data;//数据元素
struct ThreadNode *lchild,*rchild,*parent;//左右孩子指针,指向双亲的指针@@@
int ltag;//
int rtag;//左右线索标记
}ThreadNode,*ThreadTree;
PostThread
后序线索二叉树的构造, 左右根
注意体会@@@处的作用
//后序线索二叉树的构造, 左右根
void PostThread(ThreadTree &p,ThreadTree &pre){
if(p){
if(p->ltag != 1)//@@@ 这里因为p->lchild可能是p的前驱结点,//如果不判断ltag,则会死循环,
PostThread(p->lchild,pre);//递归,线索化左子树
if(p->rtag != 1)//@@@
PostThread(p->rchild, pre);//递归,线索化右子树
if(p->lchild == NULL){//左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL && pre->rchild==NULL){
pre->rchild=p;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=p;//标记当前结点成为刚刚访问过的结点
}//if(p != NULL)
}
CreatePostThread
通过后序遍历建立后序线索二叉树:
注意:因为后序遍历根为最后一个被遍历的结点,所以不能简单地用pre->rchild==NULL;而是需要判断其有无右孩子
//通过后序遍历建立后序线索二叉树的主过程算法如下:
void CreatePostThread(ThreadTree &T){
ThreadTree pre=NULL;
if(T){ //非空二叉树,线索化
PostThread(T,pre);//线索化二叉树
/*
pre->rchild==NULL;//处理遍历的最后一个结点
pre->rtag=1;
*/
if(pre->rchild==NULL){//@@@
//因为后序遍历的处理遍历的最后一个结点是整颗树的根节点,
//所以不能简单地pre->rchild==NULL
pre->rtag=1;
}
else{
pre->rtag=0;
}
//printf("CreatePostThread Finished\n");
}
}
后序线索二叉树的遍历
Firstnode
求后序线索二叉树中,后序序列下的第一个结点
思路:
- 先找到最左下结点,但此结点不一定是叶子结点,因 为它可能有右孩子,但是没有左孩子
- 再看它有无右孩子,有则 重复 1. ,即递归Firstnode
- 无右孩子,说明它就是要找的结点,return p
//求后序线索二叉树中,后序序列下的第一个结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0){//先走到最左下结点,不一定是叶子结点,
p=p->lchild;//因为它可能有右孩子,而没有左孩子
}
if(p->rtag == 0){//再看它有无右孩子,若有右孩子,则递归Firstnode
Firstnode(p->rchild);
}
else{//若无右孩子,说明它为要找的结点,return
return p;
}
}
Nextnode
求后序线索二叉树中,结点p在后序序列下的后继
1.先判断有无双亲,无双亲说明为根节点,即后序遍历的最后一个结点
2.有双亲则看它父节点有无右孩子,且自己不是右孩子!!!,(如果自己就是那个右孩子的话,递归的话会死循环,因为他们之间的指针形成了个圈),
若父节点有右孩子,则Fistenode(父节点的右孩子), 即找到父节点右孩子(父节点的右孩子可能是一棵小型子树)的后续遍历的第一个结点
3.若父节点无右孩子,说明应该遍历父节点了。任何结点都有父节点(除了根节点)
//求后序线索二叉树中,结点p在后序序列下的后继
ThreadNode *Nextnode(ThreadNode *p){
if(!p->parent){//说明p无双亲,p为根节点
printf("根节点在后序遍历中为最后一个结点\n");
return NULL;
}
if(p->parent->rtag==0 && **p->parent->rchild != p**){//右孩子指针
return Firstnode(p->parent->rchild);
}
else {// ltag==1 直接返回后继线索
return p->parent;
}
}
完整测试代码c++
//后序线索二叉树
#include<stdio.h>
#include<stdlib.h>
#define ElemType char
//tag为0表示指向左/右孩子,为1表示指向结点的前驱/后继
typedef struct ThreadNode{
ElemType data;//数据元素
struct ThreadNode *lchild,*rchild,*parent;//左右孩子指针,指向双亲的指针@@@
int ltag;//
int rtag;//左右线索标记
}ThreadNode,*ThreadTree;
void visit(ThreadTree T){
printf("%c ",T->data);
}
//后序线索二叉树的构造, 左右根
void PostThread(ThreadTree &p,ThreadTree &pre){
if(p){
if(p->ltag != 1)//@@@ 这里因为p->lchild可能是p的前驱结点,//如果不判断ltag,则会死循环,
PostThread(p->lchild,pre);//递归,线索化左子树
if(p->rtag != 1)//@@@
PostThread(p->rchild, pre);//递归,线索化右子树
if(p->lchild == NULL){//左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL && pre->rchild==NULL){
pre->rchild=p;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=p;//标记当前结点成为刚刚访问过的结点
}//if(p != NULL)
}
//通过后序遍历建立后序线索二叉树的主过程算法如下:
void CreatePostThread(ThreadTree &T){
ThreadTree pre=NULL;
if(T){ //非空二叉树,线索化
PostThread(T,pre);//线索化二叉树
/*
pre->rchild==NULL;//处理遍历的最后一个结点
pre->rtag=1;
*/
if(pre->rchild==NULL){
//因为后序遍历的处理遍历的最后一个结点是整颗树的根节点,
//所以不能简单地pre->rchild==NULL
pre->rtag=1;
}
else{
pre->rtag=0;
}
//printf("CreatePostThread Finished\n");
}
}
//求后序线索二叉树中,后序序列下的第一个结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0){//先走到最左下结点的结点,不一定是叶子结点,
p=p->lchild;//因为它可能有右孩子,而没有左孩子
}
if(p->rtag == 0){//再看它有无右孩子,若有右孩子,则递归Firstnode
Firstnode(p->rchild);
}
else{//若无右孩子,说明它为要找的结点,return
return p;
}
}
//求后序线索二叉树中,结点p在后序序列下的后继
ThreadNode *Nextnode(ThreadNode *p){
if(!p->parent){//说明p无双亲,p为根节点
//printf("根节点在后序遍历中为最后一个结点\n");
return NULL;
}
if(p->parent->rtag==0 && p->parent->rchild != p){
//右孩子指针,且右孩子不是自己,说明自己是左孩子 @@@
return Firstnode(p->parent->rchild);
}
else {// ltag==1 直接返回后继线索
return p->parent;
}
}
利用上面的两个算法,可以写出不含头结点的后序线索二叉树的后序遍历算法
void Postorder(ThreadNode *T){
for(ThreadNode *p=Firstnode(T);p!=NULL;p=Nextnode(p)){
visit(p);
}
}
//创建线索二叉树,按前序输入, #表示空节点
bool CreateThreadTree(ThreadTree &T, ThreadTree parent){
ElemType ch;
scanf("%c", &ch);
if(ch == '#'){
//printf("您要创建一棵空树吗?\n");
T=NULL;
return false;
}
else{
T=(ThreadTree)malloc(sizeof(ThreadNode));
T->ltag=T->rtag=0;
if(!T){
printf("malloc failure\n");
return false;
}
T->data = ch;
T->parent = parent;
CreateThreadTree(T->lchild,T);
CreateThreadTree(T->rchild,T);
return true;
}
}
//后序销毁 左右根
bool DestroyThreadTree(ThreadTree T){
if(T==NULL){
printf("空节点\n");
return false;
}
if(T->ltag!=1){//@@@说明左指针指向的是孩子结点,而非线索标志
DestroyThreadTree(T->lchild);
}
//printf("%c->rtag %d\n",T->data,T->rtag);
if(T->rtag!=1){//@@@
DestroyThreadTree(T->rchild);
}
printf("销毁%c\n",T->data);
free(T);//@@@'
T=NULL;
return true;
}
//后序递归遍历线索二叉树
void PostOrder(ThreadTree T){
if(T){
if(T->ltag!=1)
PostOrder(T->lchild);
if(T->rtag != 1)
PostOrder(T->rchild);
visit(T);
}
}
int main(){
ThreadTree T=NULL;
printf("按前序输入二叉树中节点的值(输入#表示空节点)\n");
CreateThreadTree(T,NULL);//输入前序,建立二叉树
CreatePostThread(T);//通过后序遍历建立先序线索二叉树
ThreadNode *p=Firstnode(T);//求后序遍历下的第一个结点
printf("\n后序遍历的第一个结点p: %c\n",p->data);
ThreadNode* r=Nextnode(p);//求后序遍历下p的后继
printf("p的后继r: %c\n",r->data);
printf("后序遍历线索二叉树(递归PostOrder ≈ 正常BinaryTree遍历): \n");
PostOrder(T);
printf("\n");
printf("\n后序遍历线索二叉树(非递归Postorder ≈ Firstnode+Nextnode): \n");
Postorder(T);
printf("\n用完要记得销毁哦!\n");
DestroyThreadTree(T);
return 0;
}
测试样例1
在输入窗口输入: AB#D##C##
测试样例2
前序输入: ABD##E##C#G##
后序输出: DEBGCA**