先序遍历建立和打印二叉树,中序遍历线索化和打印中序线索二叉树,删除中序线索二叉树的某个结点

#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
////删除中序线索二叉树的一个结点,类似于删除二叉排序树的一个结点。
//二者在删除过程中有两处不同。第一,对于中序线索二叉树,在查找元素算法中,只需返回指向要被删除元素的指针;在删除结点算法中,可以根据线索快速找到当前要被删除结点的前驱结点和后继结点。
//对于二叉排序树,在查找元素算法中,返回指向被删除元素的前驱结点的指针和被删除元素的指针,在删除结点算法中,只需要找到当前要被删除结点的前驱结点即可

//二,在中序线索二叉树中,删除叶子结点时,对线索信息的保留比较繁琐,要考虑五种情况(记住,删除的前提是当前结点为叶子结点)
//情况1,无前驱结点(就是前驱结点是NULL)有后继结点;
//情况2,有前驱结点无后继结点(就是后继结点是NULL),
//情况3,无前后驱结点(就是中序线索二叉树的根结点,前后驱结点都为NULL),
//情况4,有前后驱结点(又分为两种情况,1.当前结点的后继的左孩子是当前结点,2.当前结点的前驱的右孩子是当前结点)
//若删除的是非叶子结点,要分三种情况讨论,情况1,有左子树无右子树,情况2,有右子树无左子树,情况三,有左右子树。

//总的来说,中序线索二叉树是通过递归的方式判断并删除结点,删除操作在叶子结点上进行,而二叉排序树删除操作可以在叶子结点上或者双亲结点上进行。

 

你可能感兴趣的:(先序遍历建立和打印二叉树,中序遍历线索化和打印中序线索二叉树,删除中序线索二叉树的某个结点)