• 数据结构-二叉树(8)线索化二叉树


    1.中序线索二叉树

    为了区分线索和左右指针,在每个结点中设置标准ltag和rtag。

    ltag==0时,表明ltag域中存放的是指向左子女的指针,否则是指向该结点中序下前驱的线索

    寻找当前结点在中序序列下的后继:

     

    rtag==0(右子女指针)

    rtag==1(后继线索)

    rightChild==NULL

    无此情况

    无后继

    rightChild!=NULL

    后继为当前结点右子女中序下第一个结点

    后继为右子女结点

    寻找当前结点在中序序列下的前驱

     

    ltag==0(左子女指针)

    ltag==1(后继线索)

    leftChild==NULL

    无此情况

    无前驱

    leftChild!=NULL

    前驱为当前结点左子女中序下最后一个结点

    前驱为左子女结点

    线索化的过程

    定义了两个函数createInThread()和createInThread(ThreadNode<T> *current, ThreadNode<T> &*pre)

    createInThread(ThreadNode<T> *current, ThreadNode<T> &*pre)执行过程中pre指针始终指向遍历指针指向的当前结点前驱结点,执行后pre指针指向以current为根的树中序下最后一个结点

    createInThread()函数创建pre指针并调用createInThread(root, &pre)来线索化整棵树

    1)判断current是否为NULL,是的话直接返回

    2)调用createInThread(current->leftChild,pre)对左子树进行线索化

    3)如果pre不为NULL且pre指向的结点没有右子女,就要为其建立后继线索

    4)current变为新的pre,调用createInThread(current->rightChild,pre)对右子树线索化

    寻找t结点的父结点的过程:

    1)如果t是根,则没有父结点,直接返回NULL

    2)首先让p指针从t一直往左下方走,一直走到最左下方的结点,即以t为根的树在中序下的第一个结点(这个结点可能是没有中序前驱线索的结点,即整棵树在中序下的第一个结点,此时该方法失败)。沿着线索走到位于树上层的中序前驱,然后往右下方寻找父结点。有可能会找不到。

    4)试探第二条路径,让p指针从t一直往右下方走,一直走到最右下方的结点,即以t为根的树在中序下的最后一个结点(这个结点可能是没有中序前驱后继的结点,即整棵树在中序下的最后一个结点,此时该方法失败)。沿着线索走到位于树上层的中序后续,然后往左下方寻找父结点。该路径一定会找到t的父结点

    template <class T>
    struct ThreadNode{
        //线索二叉树的结点类
        int ltag,rtag;
        ThreadNode<T> *leftChild,*rightChild;
        T data;
        ThreadNode(const T item):data(item),leftChild(NULL),rightChild(NULL),ltag(0),rtag(0)();
    };
    
    template <class T>
    class ThreadTree{
    protected:
        ThreadNode<T> *root;
        void createInThread(ThreadNode<T> *current,ThreadNode<T> *&pre);
        ThreadNode<T> *parent(ThreadNode<T> *t);   //寻找t结点的父结点
    public:
        ThreadTree():root(NULL){};
        void createInThread();
        ThreadNode<T> *First(ThreadNode<T> *current);
        ThreadNode<T> *Last(ThreadNode<T> *current);
        ThreadNode<T> *Next(ThreadNode<T> *current);
        ThreadNode<T> *Prior(ThreadNode<T> *current);
        void Inorder(void (*visit)(ThreadNode<T> *p));
        void Preorder(void (*visit)(ThreadNode<T> *p));
        void Postorder(void (*visit)(ThreadNode<T> *p));
        void InsertRight(ThreadNode<T> *s, ThreadNode<T> *r);
        bool ThreadTree<T>::deleteRight(ThreadNode<T> *s);
    }
    
    template <class T>
    ThreadNode<T> *ThreadTree<T>::First(ThreadNode<T> *current){
        //返回以*current为根的中序线索二叉树中中序序列下的第一个结点
        ThreadNode<T> *p=current;
        while(p->ltag==0) p=p->leftChild;
        return p;
    }
    
    template <class T>
    ThreadNode<T> *ThreadTree<T>::Next(ThreadNode<T> *current){
        //函数返回在中序线索二叉树中结点current在中序下的后继结点
        ThreadNode<T> *p=current->rightChild;
        if(current->rtag==0) return First(p);  //rtag==0且右子树存在,后续为右子女在中序下第一个结点
        else return p;  //rtag==1且右子树p存在,后继为p;rtag==1且右子树p为NULL,后继为NULL
    }
    
    template <class T>
    ThreadNode<T> *ThreadTree<T>::Last(ThreadNode<T> *current){
        //返回以*current为根的中序线索二叉树中中序序列下的最后一个结点
        ThreadNode<T> *p=current;
        while(p->rtag==0) p=p->right;
        return p;
    }
    
    template <class T>
    ThreadNode<T> *ThreadTree<T>::Prior(ThreadNode<T> *current){
        //返回以*current在中序序列下的前驱
        ThreadNode<T> *p=current->leftChild;
        while(current->ltag==0) return Last(p);  //ltag==0且左子树存在,前驱为左子女中序下第一个结点
        else return p;   //ltag==1且左子树p存在,前驱为p;ltag==1且左子树为NULL,前驱为NULL
    }
    
    template <class T>
    void ThreadTree<T>::Inorder(void (*visit)(ThreadNode<T> *p)){
        //先用First()方法找到二叉树在中序序列下的第一个结点,把它作为当前结点,再利用求后继点的运算Next()按照中序次数逐个访问直到二叉树最后一个结点
        ThreadNode<T> *p;    for(p=First(root);p!=NULL;p=Next(p)) visit(p);
    }
    
    template <class T>
    void ThreadTree<T>::Preorder(void (*visit)(ThreadNode<T> *p)){
        ThreadNode<T> *p=root;
        while(p!=NULL){
            visit(p);
            if(p->ltag==0) p=p->leftChild;
            else if(p->rtag==0) p->rightChild;
            else{     //说明p是叶结点
                while(p!=NULL && p->rtag==1) p=p->rightChild;    //沿着中序后继线索走到一个有右子女结点的结点
                if(p!=NULL) p=p->rightChild;                     //这个右子女结点就是当前结点的前序后继结点
             }
        }
    }
    
    template <class T>
    void ThreadTree<T>::Postorder(void (*visit)(ThreadNode<T> *p)){
        ThreadNode<T> *t=root;
        while(t->ltag==0||t->rtag==0)     //从根结点出发,沿着左子女链一直找,找到左子女不再是左子女指针的结点,再找该结点的右子女。重复直到找到叶结点
            if(t->ltag==0) t=t->leftChild;
            else if(t->rtag==0) t=t->rightChild;
        visit(t);
        ThreadNode<T> *p;
        while(p=parent(t)!=Null){
            if(p->rightChild==t||p->rtag==1) t=p;    //如果当前结点*t是父结点*p的右子女,或者虽然当前结点*t是父结点*p的左子女但父结点没有右子女,则父结点*p是*t的后序后继
            else{
                t=p->rightChild;       //否则,t指针移动到*p的右子女,继续出发,沿着左子女链一直找,找到左子女不再是左子女指针的结点,再找该结点的右子女。重复直到找到叶结点
                while(t->ltag==0||t->rtag==0)
                    if(t->ltag==0) t=t->leftChild;
                    else if(t->rtag==0) t=t->rightChild;
            }
            visit(t);
        }
    }
    
    template <class T>
    void ThreadTree::createInThread(){
        ThreadNode<T> *pre=NULL;   //前驱结点指针
        if(root!=NULL){
            createInThread(root,&pre);
            pre->rightChild=NULL;     //createInThread(root,&pre)执行后pre变成了树在中序下最后一个结点
            pre->rtag=1;
        }
    }
    
    template <class T>
    void ThreadTree::createInThread(ThreadNode<T> *current,ThreadNode<T>* &pre){
        //通过中序遍历,对二叉树进行线索化,pre总指向遍历指针p在中继下的前驱结点。
        if(current==NULL) return;
        createInThread(current->leftChild,pre);
        if(current->leftChild==NULL){     //没有左子女时,建立当前结点的前驱线索
            current->leftChild=pre;
            current->ltag=1;
        }
        if(pre!=NULL && pre->rightChild=NULL){    //建立前驱结点的后继线索
            pre->rightChild=current;
            pre->rtag=1;
        }
        pre=current;
        createInThread(current->rightChild,pre);
    }
    
    template <class T>
    ThreadNode<T> *ThreadTree::parent(ThreadNode<T> *t){
        ThreadNode<T> *p;
        if(t==root) return NULL;   //根结点没有父结点
        for(p=t;p->ltag==0;p=p->leftChild);  //从当前结点走到树上层的一个中序前驱,然后向右下找父结点
        if(p->leftChild!=NULL)      //p->leftChild==NULL时,说明走到了没有前驱结点的最左下第一个结点
            for(p=p->leftChild; p!=Null&&p->leftChild!=t&&p->rightChild!=t; p=p->rightChild); //没找到的话p==NULL
        if(p==NULL||p->leftChild==NULL){          //如果没找到、或者刚刚走到了中序第一个结点,则从第二条路径寻找
            for(p=t;p->rtag==0;p=p->rightChild);   //从当前结点走到树上层的一个中序后继,然后向左下找父结点。可能会走到没有后继结点的最右下最后一个结点,此时该方法失败。
            for(p=p->rightChild; p!=NULL&&p->leftChild!=t&&p->rightChild!=t; p=p->leftChild);
        }
        return p;
    }
    
    template <class T>
    void ThreadTree<T>::InsertRight(ThreadNode<T> *s, ThreadNode<T> *r){
        //将当前结点*r当作*s的右子女插入
        r->rightChild=s->rightChild; r->rtag=s->tag;
        r->leftChild=s; r->ltag=1;
        s->rightChild=r; s->rtag=0;
        if(r->tag==0){
            ThreadNode<T> *temp;
            temp=First(r->rightChild);   //如果原结点s的rightChild是线索,寻找s原来的右子女中序下第一个结点
            temp->leftChild=r;           //为其建立前驱线索
        }
    }
    
    template <class T>
    bool ThreadTree<T>::deleteRight(ThreadNode<T> *s){
        //删除结点s的右子女r
        if(s->rtag==1) return false;   //如果结点s没有右子女,删除失败
        ThreadNode<T> *r=s->rightChild;
        if(r->leftChild==NULL && r->rightChild==NULL){  //如果结点r是叶结点,重新链接后继线索
            s->rightChild=r->rightChild;
            s->rtag=1;
        }
        if(r->leftChild==NULL && r->rightChild!=NULL){  //如果结点r仅有右子树,必须重新链接右子树到父结点s下
            ThreadNode<T> *fr=First(r->rightChild);
            fr->leftChild=r->leftChild;
            s->rightChild=r->rightChild;
        }
        if(r->leftChild!=NULL && r->rightChild==NULL){  //如果结点r仅有左子树,必须重新链接左子树到父结点s下
            ThreadNode<T> *la=Last(r->leftChild);
            la->rightChild=r->rightChild;
            s->rightChild=r->leftChild;
        }
        if(r->leftChild!=NULL && r->rightChild!=NULL){  //如果结点r既有左子树又有右子树
            ThreadNode<T> *la=Last(r->leftChild);
            la->rightChild=r->rightChild;
            la->rtag=r->tag;
            s->rightChild=r->leftChild;
            ThreadNode<T> *fr=First(r-rightChild);
            fr->leftChild=la;
        }
        return true;
    }

    2.前序线索二叉树

    3.后序线索二叉树

    后序线索二叉树的遍历需要借助栈,因为可能某结点的后继是父节点,但是它有右子女无法存储后继线索。

  • 相关阅读:
    阅读笔记——增强学习3
    阅读笔记——增强学习2
    阅读笔记——增强学习1
    阅读笔记十六
    阅读笔记十五
    MVC实例应用模式
    MVC浅谈
    设计模式理解
    某系统质量属性设计实现详述
    《大型网站架构》浅读有感
  • 原文地址:https://www.cnblogs.com/yangyuliufeng/p/9453715.html
Copyright © 2020-2023  润新知