#include"stdio.h" #include"stdlib.h" typedef struct TBTNode { int data; int ltag,rtag;//ltag == 0表示存在左孩子 ,ltag == 1表示存在前驱线索 struct TBTNode *lchild; struct TBTNode *rchild; } TBTNode; //先序遍历建立二叉树 void createBTree(TBTNode* &bt) { int a; scanf("%d",&a); if(a == -1) ; else { bt=(TBTNode*)malloc(sizeof(TBTNode)); bt->data = a; bt->ltag= 0; bt->rtag = 0; //结点是否有线索?1:有,0:没有。建立线索二叉树时,默认0 bt->lchild = NULL; bt->rchild = NULL; createBTree(bt->lchild); createBTree(bt->rchild); } } //先序遍历输出二叉树 void preorder(TBTNode *bt) { if(bt!= NULL) { printf("%d ",bt->data); preorder(bt->lchild); preorder(bt->rchild); } } //中序线索化二叉树,使之成为中序线索二叉树 void inorderCreateTBTree(TBTNode *&p,TBTNode* &pre) { if(p != NULL) { inorderCreateTBTree(p->lchild,pre); if(p->lchild == NULL) { //建立当前结点的前驱线索 p->ltag = 1; p->lchild = pre; } if(pre!= NULL &&pre->rchild == NULL) { //建立前驱结点的后继线索;pre != NULL必须放在前面, 否则,刚开始pre==NULL,后面的pre->rchild 会报错 pre->rtag = 1; pre->rchild = p; } pre = p; //前驱结点记录当前结点 inorderCreateTBTree(p->rchild,pre); } } //中序线索二叉树一旦建立完成,只能根据 ltag或者rtag 来区分叶子节点,非叶子节点,左孩子以及右孩子。 //比如,未线索化二叉树的叶子节点其左右孩子都指向NULL,建立中序线索二叉树后,叶子节点 的左孩子可以指向前驱,右孩子可以指向后继。 //查找第一个结点,即根结点的最左下结点 TBTNode *first(TBTNode *p) { if(p!=NULL) { while(p->ltag == 0) p = p->lchild; } return p; } //查找最后一个结点,即根结点的最右下结点 TBTNode *last(TBTNode *p) { if(p!= NULL) { while(p->rtag == 0) p = p->rchild; } return p; } //查找当前结点的后继结点,即其右孩子的最左下结点 TBTNode *next(TBTNode *p) { if(p!= NULL) { if(p->rtag == 0) return first(p->rchild); } return p->rchild; } //查找当前结点的前驱结点,即其左孩子的最右下结点 TBTNode *prefind(TBTNode *p) { if(p!= NULL) { if(p->ltag == 0) return last(p->lchild); } return p->lchild; } //中序遍历中序线索二叉树 void inorder(TBTNode* root) { for(TBTNode* p=first(root); p!=NULL; p=next(p)) { printf("%d ",p->data); } } //删除中序线索二叉树的一个结点 -----start //第一步:找到要删除结点p,若p存在,执行第二步,否则,算法结束 TBTNode* findTBTnode(TBTNode *root,int e) { for(TBTNode* p=first(root); p!=NULL; p=next(p)) { if(p->data == e) return p; } } //第二步:情况1.若当前p为叶子结点,(归根结底,删除操作只在叶子结点上进行)要根据“五种不同的叶子结点情况(写在末尾)”删除p结点,算法结束; //情况2.若p有左孩子无右孩子,那么找到其前驱结点pre,把pre的结点值赋值到p上,对pre结点进行判断删除,即令p = pre再执行第二步; //情况3.若p有右孩子无左孩子,那么找到其后继结点n,把n的结点值赋值到p上,对n结点进行判断删除,即令p = n再执行第二步; //情况4.若p有左右孩子,那么除了这句话开头的条件发生了变化,执行步骤可以按情况2或者情况3进行。这里选择情况三,即找到p的后继结点n,把n的结点值赋值到p上,对n结点进行判断删除,即令p = n再执行第二步。 void deleteTBTNode(TBTNode *&bt,TBTNode *&p) { //因为要对线索二叉树进行改变,这里使用引用型指针变量 if(p!= NULL) { TBTNode* pre,*n; pre = prefind(p); n = next(p); if(p->ltag == 1 && p->rtag == 1) { //当前结点是叶子结点 if(p == first(bt) && n != NULL) { //情况1:无前驱结点有后继结点 //指针变量的值表示地址 n->ltag = p->ltag; n->lchild = p->lchild; } else if(p == last(bt) && pre != NULL) { //情况2:若有前驱结点无后继结点 pre->rtag = p->rtag; pre->rchild = p->rchild; } else if(n&&pre&& p == pre->rchild) { //情况3:若当前结点的前驱是其双亲结点 pre->rtag = p->rtag; pre->rchild = p->rchild; } else if(n&&pre&& p == n->lchild) { //情况4:若当前结点的后继是其双亲结点 n->ltag = p->ltag; n->lchild = p->lchild; } if(p == bt) bt = NULL; //情况5:中序线索二叉树只有一个结点,即根节点时 free(p); //释放指针所指向的内存空间,然后设置指针为NULL p = NULL; } else if(p->ltag == 0 && p->rtag == 1) { // 当前结点有左孩子无右孩子 p->data = pre->data; deleteTBTNode(bt,pre); } else if(p->ltag == 1 && p->rtag == 0) { //当前结点有右孩子无左孩子 p->data = n->data; deleteTBTNode(bt,n); } else { //当前结点既有右孩子又有左孩子 p->data = n->data; deleteTBTNode(bt,n); } } } //删除中序线索二叉树的一个结点 -----end int main() { TBTNode *bt; printf("先序遍历建立和打印二叉树\n"); createBTree(bt); preorder(bt); printf("\n"); printf("中序遍历线索化和打印中序线索二叉树\n"); TBTNode *pre = NULL; if(bt!= NULL) { //中序线索化二叉树 inorderCreateTBTree(bt,pre); //末尾结点处理 pre->rtag = 1; pre->rchild = NULL; } inorder(bt); printf("\n"); printf("删除中序线索二叉树的某个结点\n"); TBTNode *p; int a; while(bt!= NULL) { scanf("%d",&a); p = findTBTnode(bt,a); deleteTBTNode(bt,p); inorder(bt); printf("\n"); } // preorder(bt); 二叉树线索化后只能通过线索遍历 return 0; } //输入格式:1 2 3 -1 -1 4 5 -1 -1 6 -1 -1 7 -1 8 -1 -1 //输出格式: //先序遍历建立和打印二叉树 //1 2 3 4 5 6 7 8 //中序遍历线索化和打印中序线索二叉树 //3 2 5 4 6 1 7 8 //删除中序线索二叉树的某个结点 //3 //2 5 4 6 1 7 8 //2 //5 4 6 1 7 8 //5 //4 6 1 7 8 //4 //6 1 7 8 //6 //1 7 8 //1 //7 8 //7 //8 //8 //删除中序线索二叉树的一个结点,类似于删除二叉排序树的一个结点。 //二者在删除过程中有两处不同。第一,对于中序线索二叉树,在查找元素算法中,只需返回指向要被删除元素的指针;在删除结点算法中,可以根据线索快速找到当前要被删除结点的前驱结点和后继结点。 //对于二叉排序树,在查找元素算法中,返回指向被删除元素的前驱结点的指针和被删除元素的指针,在删除结点算法中,只需要找到当前要被删除结点的前驱结点即可 //二,在中序线索二叉树中,删除叶子结点时,对线索信息的保留比较繁琐,要考虑五种情况(记住,删除的前提是当前结点为叶子结点) //情况1,无前驱结点(就是前驱结点是NULL)有后继结点; //情况2,有前驱结点无后继结点(就是后继结点是NULL), //情况3,无前后驱结点(就是中序线索二叉树的根结点,前后驱结点都为NULL), //情况4,有前后驱结点(又分为两种情况,1.当前结点的后继的左孩子是当前结点,2.当前结点的前驱的右孩子是当前结点) //若删除的是非叶子结点,要分三种情况讨论,情况1,有左子树无右子树,情况2,有右子树无左子树,情况三,有左右子树。 //总的来说,中序线索二叉树是通过递归的方式判断并删除结点,删除操作在叶子结点上进行,而二叉排序树删除操作可以在叶子结点上或者双亲结点上进行。