数据结构-二叉树(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.后序线索二叉树

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

posted @ 2018-08-10 10:56  扬羽流风  阅读(610)  评论(0编辑  收藏  举报