分析:
/*
* 用中序线索二叉树查找二叉树结点的中序前驱和中序后继
* (1)创建一个线索二叉树
* (2)对这个线索二叉树进行中序线索化 ====>> 变成一个中序线索二叉树
* (3)利用中序线索二叉树的线索查找该结点的中序前驱和中序后继
*
*/
/*
* 中序线索化(左根右):
* (a)如果一个结点没有左子树,那么这个结点的前驱肯定不是自己的左子树(NULL),而是按照中序遍历的顺序,这个结点的前驱肯定是在这个结点的前面一个遍历
*的结点(pre)---中序线索化中不管该节点有没有左子树,该节点的前驱肯定就是该节点的前面一个被中序遍历的结点。所以 T->lchild=pre ;
* (b)如果一个结点没有右子树,那么这个结点的后继肯定不是自己的右子树(NULL),而是按照中序遍历的舒徐,这个节点的后继肯定是中序遍历中
*该节点的后面一个被遍历到的,---中序线索化中不管该节点有没有右子树。当正在访问当前这个节点时,该节点的后继是肯定是还未被访问到的
*(后面一次的访问才是访问该节点的后继),
*所以,要连接上该节点的后继,应该时在下一个访问结点时,这时,正在访问的结点(T)肯定是之前结点(pre)的后继结点,
* 所以:当pre结点没有右子树时,pre结点的后继结点就是中序遍历的当前结点T,把当前结点T的地址赋给pre的后继指针:
*if(pre->lchild=NULL&&pre!=NULL)
* {pre->rchild=T;}
* (c)
* //其他的情况是有左孩子或有右孩子,
//这些结点的中序前驱就是这些结点的左子树的最右下的结点;
//这些结点的中序后继就是这些结点的右子树的最左下的结点
*
*/
代码:
#include <stdio.h> #include <malloc.h> #define MAX 10 //(1)创建一个中序线索二叉树 //定义线索二叉树结点 typedef struct BiThread { int data; int ltag;//该节点有左子树:0;没有左子树(NULL/已经线索化):1;//创建线索二叉树时,默认都为0 //在中序线索化该二叉树时,才会将左右子树为NULL的赋1 int rtag;//该节点有右子树:0;没有右子树(NULL/已经线索化):1;//创建线索二叉树时,默认都为0 struct BiThread* lchild;//该节点的左子树 struct BiThread* rchild;//该节点的右子树 }BiTnode,* BiThread_Tree; BiTnode* pre = NULL;//全局变量,指向线索二叉树当前正在访问(遍历)的结点T的 前一个已经被遍历的结点 //(pre:用于记录当前正在遍历的结点的前一个已经被遍历过的结点的地址)//前一个足迹 /*要实现这个的话,还得再搞3个队列(用下面定义的数组的话是完成不了的),懒得搞,把你注释掉*/ //BiTnode* du_1_have_pre[MAX];//存储已经被中序线索化之后的ltag=1的结点地址,(可以直接访问前驱的结点) //BiTnode* du_1_have_post[MAX];//存储已经被中序线索化之后的rtag=1的结点地址,(可以直接访问后继的结点) //BiTnode* leaf[MAX];//存储已经被中序线索化之后的ltag=1&&rtag=1的结点地址,(可以直接访问前驱和后继的结点) //初始化中序线索二叉树的根节点 BiThread_Tree Init_BiThread_root() { int e; BiThread_Tree N = (BiThread_Tree)malloc(sizeof(BiTnode)); printf("\n请输入该中序线索二叉树的根节点的值:"); scanf_s("%d",&e); N->data = e; N->lchild = NULL; N->rchild = NULL; N->ltag = 0; N->rtag = 0; printf("\n根节点创建成功!"); return N; } //为线索二叉树的结点T插入左孩子 void insert_lnode(BiTnode* &T) { int flag = 1; printf("\n %d->lchild==NULL--1;%d->lchild==(int)--2:",T->data,T->data); scanf_s("%d",&flag); if (flag==2) { int e; BiTnode* child = (BiTnode*)malloc(sizeof(BiTnode)); printf("\n请输入 %d->lchild->data: ", T->data); scanf_s("%d", &e); child->data = e; child->lchild = NULL; child->rchild = NULL; child->ltag = 0; child->rtag = 0; T->lchild = child; } else//T->lchild==NULL { //本来初始化每一个结点的时候就都设置为NULL的,所有这里就不需要操作了 } } //为线索二叉树的结点T插入右孩子 void insert_rnode(BiTnode*& T) { int flag = 1; printf("\n %d->rchild==NULL--1;%d->rchild==(int)--2:",T->data,T->data); scanf_s("%d", &flag); if (flag == 2) { int e; BiTnode* child = (BiTnode*)malloc(sizeof(BiTnode)); printf("\n请输入 %d->rchild->data: ", T->data); scanf_s("%d", &e); child->data = e; child->lchild = NULL; child->rchild = NULL; child->ltag = 0; child->rtag = 0; T->rchild = child; } else//T->rchild==NULL { //本来初始化每一个结点的时候就都设置为NULL的,所有这里就不需要操作了 } } //用递归实现创建一颗完整的线索二叉树(还未实现中序线索化) void creat_BiThread_Tree(BiThread_Tree &T) { if (T!=NULL) { insert_lnode(T);//插入结点T的左孩子 creat_BiThread_Tree(T->lchild); insert_rnode(T);//插入结点T的右孩子 creat_BiThread_Tree(T->rchild); } } //(2)对这个线索二叉树进行中序线索化 ====>> 变成一个中序线索二叉树 //中序线索化是以中序遍历的顺序为基准,为已经创建好的线索二叉树 连接 中序线索(每个结点连接中序前驱和中序后继), // //中序线索二叉树:可以直接找到该结点的中序前驱和中序后继的线索二叉树 //对线索二叉树进行中序线索化 void visit_BiTnode(BiThread_Tree T) { printf("\n %p :%d",T,T->data); if (T->lchild == NULL) { T->lchild=pre; T->ltag = 1;//结点T的左孩子已经被中序线索化了 } if (pre!=NULL&&pre->rchild==NULL) { pre->rchild=T; pre->rtag = 1;//结点T的右孩子已经被中序线索化了 } pre=T;//每次访问完当前结点T,就把T的地址赋给pre,T指向下一个被中序遍历的结点 //其他的情况是有左孩子或有右孩子, //这些结点的中序前驱就是这些结点的左子树的最右下的结点; //这些结点的中序后继就是这些结点的右子树的最左下的结点 } //一边中序遍历一边对遍历到的结点中序线索化 void IN_Thread(BiThread_Tree &T) { if (T != NULL) { IN_Thread(T->lchild);//左 visit_BiTnode(T);//根 IN_Thread(T->rchild);//右 } } void creat_IN_Thread(BiThread_Tree &T) { printf("\n线索二叉树开始中序线索化:。。。"); if (T != NULL)//树不是空的 { IN_Thread(T); } if (pre->rchild == NULL) { pre->rtag=1;//最后一个被中序遍历的结点实现中序线索化 } printf("\n线索二叉树中序线索化完成!"); } //(3)利用中序线索二叉树的线索查找该结点的中序前驱和中序后继 //结点T的中序前驱必定是该节点T的左子树的最右下的结点(结点T的中序前驱结点是 结点T左子树 最后一个被中序遍历到的结点,之后下一个被中序遍历到的就是结点T) //结点T的中序后继必定是该节点T的右子树的最左下的结点(结点T的中序后继结点是 结点T右子树 第一个被中序遍历到的结点,所以前一个被中序遍历到的就是结点T) //找中序线索二叉树的结点T的中序前驱(左子树的最后一个被中序遍历到的结点) //得到在以结点T为根的树中 被最后一个中序遍历(左根右)到的结点(最右下的结点) BiTnode* LastNode(BiTnode* T) { while (T->rtag == 0)//结点有右孩子 T=T->rchild;//最后一个被中序遍历到的为结点的右孩子 //结点没有右孩子(T->rtag==1),该子树的最后一个被中序遍历的结点就是根节点T本身 return T; } //得到结点T的中序前驱(结点T的左子树的最后一个被中序遍历访问到的结点) BiTnode* PreNode(BiTnode* T) { if (T->ltag == 0)//如果结点T有左子树,结点T的中序前驱(结点T的左子树的最后一个被中序遍历访问到的结点) { return LastNode(T->lchild); } else if (T->ltag == 1)//如果结点T没有左子树,结点T的中序前驱就是T->lchild (中序线索化后的后继) return T->lchild; } void visit_IN(BiTnode* p) { printf(" %d ",p->data); } //不用递归,利用中序线索二叉树实现二叉树的中序遍历的逆序输出 void put_out_inverse(BiThread_Tree T) { BiTnode* p=NULL; for (p= LastNode(T);p!=NULL;p= PreNode(p)) { visit_IN(p); } } //找到结点T的中序后继结点(结点T的右子树的第一个被中序遍历到的结点(右子树的最左下的结点)) //得到以结点T为根节点的二叉树的的第一个被中序遍历(左根右)到的结点 BiTnode* FistNode(BiTnode* T) { while (T->ltag == 0)//结点有左孩子 { T=T->lchild; } //结点没有左孩子(T->ltag==1),该二叉树的第一个被中序遍历到的结点就是根节点本身(左根右) return T; } //得到结点T的中序后继结点(结点T的右子树的第一个被中序遍历到的结点) BiTnode* PostNode(BiTnode* T) { if (T->rtag == 0)//有右子树:返回结点T的右子树的第一个被中序遍历到的结点 return FistNode(T->rchild); else if (T->rtag == 1) return T->rchild;//没右子树 } //不用递归,利用中序线索二叉树实现二叉树的中序遍历的顺序输出 void put_out(BiThread_Tree T) { BiTnode* p = NULL; for (p = FistNode(T); p != NULL; p = PostNode(p)) { visit_IN(p); } } void test_02() { BiTnode* p = NULL; BiTnode* node = NULL; BiThread_Tree T=Init_BiThread_root(); creat_BiThread_Tree(T); printf("\n线索二叉树创建成功!(未中序线索化)"); creat_IN_Thread(T);//完成中序线索化 printf("\n请输入你想要查找中序前驱的结点的!地址!:"); scanf_s("%p",&p); node = PreNode(p); printf("\n %p :%d 的中序前驱:\n %p : %d ",p,p->data,node,node->data); printf("\n请输入你想要查找中序后继的结点的!地址!:"); scanf_s("%p", &p); node = PostNode(p); printf("\n %p :%d 的中序前驱:\n %p : %d ", p, p->data, node, node->data); printf("\n利用中序线索二叉树得到顺序中序遍历:\n"); put_out(T); printf("\n利用中序线索二叉树得到逆序中序遍历:\n"); put_out_inverse(T); } int main() { test_02(); return 0; }
运行结果:
手算: