数据结构-二叉树(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.后序线索二叉树
后序线索二叉树的遍历需要借助栈,因为可能某结点的后继是父节点,但是它有右子女无法存储后继线索。