中序线索二叉树创建及其遍历


通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数。准确的说,n各结点的二叉链表共有2n个链域,非空链域为n-1个,但其中的空链域却有n+1个。如下图所示。


    因此,提出了一种方法,利用原来的空链域存放指针,指向树中其他结点。这种指针称为线索。

    记ptr指向二叉链表中的一个结点,以下是建立线索的规则:

    (1)如果ptr->lchild为空,则存放指向中序遍历序列中该结点的前驱结点。这个结点称为ptr的中序前驱;

    (2)如果ptr->rchild为空,则存放指向中序遍历序列中该结点的后继结点。这个结点称为ptr的中序后继;

    显然,在决定lchild是指向左孩子还是前驱,rchild是指向右孩子还是后继,需要一个区分标志的。因此,我们在每个结点再增设两个标志域ltag和rtag,注意ltag和rtag只是区分0或1数字的布尔型变量,其占用内存空间要小于像lchild和rchild的指针变量。结点结构如下所示。


    其中:

    (1)ltag为0时指向该结点的左孩子,为1时指向该结点的前驱;

    (2)rtag为0时指向该结点的右孩子,为1时指向该结点的后继;

    (3)因此对于上图的二叉链表图可以修改为下图的养子。

1.树节点采用class定义,class bintree{};

 

class bintree
{
public:
char data;
bintree
*leftchild,*rightchild;//存储左右节点
int lefttag,righttag;//标记左右节点是指针还是线索,1表示是线索,0表示是指针
};

 

2.首先创建一颗二叉树

输入包括一行,为由空格分隔开的各节点,按照二叉树的分层遍历顺序给出,每个节点形式如X(Y,num),X表示该节点,Y表示父节点,num为0,1,2中的一个,0 表示根节点,1表示为父节点的左子节点,2表示为父节点的右子节点。输出为一行,为中序遍历的结果.

输入:A(0,0) B(A,1) C(A,2) D(B,1) E(B,2) F(C,1) G(D,1) H(D,2)

输出:G D H B E A F C

创建二叉树的代码可以根据自己的习惯自己编写函数。以下为个人习惯:

bintree *createtree(){
bintree
*root = new bintree;
bintree
*parent;
string in_string;
while(cin>>in_string){
if(in_string[2]-'0'==0){
root
->data = in_string[0];
root
->leftchild = root->rightchild = NULL;
root
->lefttag = root->righttag=0;
continue;
}
parent
= find(root,in_string[2]);
if(in_string[4]-'0'==1){
bintree
*newnode = new bintree;
newnode
->data = in_string[0];
newnode
->leftchild = newnode->rightchild = NULL;
newnode
->lefttag = newnode->righttag=0;
parent
->leftchild = newnode;
}
else if(in_string[4]-'0'==2){
bintree
*newnode = new bintree;
newnode
->data = in_string[0];
newnode
->leftchild = newnode->rightchild = NULL;
newnode
->lefttag = newnode->righttag = 0;
parent
->rightchild = newnode;
}
if(getchar()=='\n')
break;
}
return root;
}

 

 

3.写出中序线索化过程的函数,在添加头结点线索化函数中调用

因为要线索化当前节点的后继不方便,所以用pre记录节点t的前一个节点,可以确定t为pre的后继,那么,每次都是确定当前节点的前驱和该节点上一个节点pre的后继(参照代码)。

bintree *pre;//用来记录上一个节点,即当前节点的前驱,当前节点为pre的后继
bintree *inthreading(bintree *root){ bintree *t = root;
if(t){
inthreading(t
->leftchild);//线索化左子树
      /****!!!!!****/
if(t->leftchild==NULL){//左子树为空,则将其线索化,使其指向它的前驱
t
->lefttag = 1;
t
->leftchild = pre;
}
      //因为要线索化当前节点的后继不方便,所以用pre记录节点t的前一个节点,可以确定t为pre的后继,那么,每次都是确定当前节点的前驱和该节点上一个节点pre的后继
if(pre->rightchild==NULL){//前一个节点右子树为空,则将其线索化,使其指向它的后继
pre
->righttag = 1;
pre
->rightchild = t;
}
pre
= t;
      /****!!!!!*****/ inthreading(t
->rightchild);//线索化右子树
}
return root;
}

 

4.通过添加头结点的方式将其线索化

上述/***!!!!!****/    /****!!!!!****/中间部分代码做了这样的事情:

    if(!p->leftchild)表示如果某结点的左指针域为空,因为其前驱结点刚刚访问过,赋值了pre,所以可以将pre赋值给p->leftchild,并修改p->lefttag = 1(也就是定义为1)以完成前驱结点的线索化。

    后继就麻烦一些。因为此时p结点的后继还没 有访问到,因此只能对它的前驱结点pre的右指针rightchild做判断,if(!pre->rightchild)表示如果为空,则p就是pre的后继,于是 pre->rightchild = p,并且设置pre->righttag =1,完成后继结点的线索化。

    完成前驱和后继的判断后,不要忘记当前结点p赋值给pre,以便于下一次使用。

    

    有了线索二叉树后,对它进行遍历时,其实就等于操作一个双向链表结构。

    和双向链表结点一样,在二叉树链表上添加一 个头结点,如下图所示,并令其leftchild域的指针指向二叉树的根结点(图中第一步),其rightchild域的指针指向中序遍历访问时的最后一个结点(图中第 二步)。反之,令二叉树的中序序列中第一个结点中,leftchild域指针和最后一个结点的rightchild域指针均指向头结点(图中第三和第四步)。这样的好处 是:我们既可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历。

 

 

 

 

bintree *addheadthread(bintree *root,bintree *head){
bintree
*t = root;
head
->righttag=0;
head
->rightchild = head;
if(t==NULL){
head
->lefttag = 0;
head
->leftchild = head;
}
else{
pre
= head;
head
->lefttag = 0;
head
->leftchild = t;//完成图中所示的步骤1
inthreading(t);//在该过程中就已经完成了线索化和图中所示的步骤3
pre
->rightchild = head;//完成步骤4
pre
->righttag = 1;
head
->rightchild = pre;//完成步骤2
}
}

 

 

 

5.中序遍历线索化二叉树

 

void inorder(bintree *head){
bintree
*t = head->leftchild;//通过头结点进入根节点
while(t!=head){//表示已遍历完成,指针t已经回到了头结点
while(t->lefttag==0)//循环找到中序遍历的第一个节点
t
= t->leftchild;
cout
<<t->data<<" ";
while(t->righttag==1&&t->rightchild!=head){//不断的输出后继,直到某个节点rightchild所指的不是后继,即righttag!=1
t
= t->rightchild;
cout
<<t->data<<" ";
}
t
= t->rightchild;//进入右子树
}
}

 

 

 

6.完整代码

 

/***********线索二叉树************
Author:ChengSong
Language:C++
Time:2015/12/23
*******************************
*/
#include
<iostream>
#include
<cstdlib>
#include
<string>
#include
<cstdio>
#include
<stack>
#define type char
using namespace std;
class bintree
{
public:
char data;
bintree
*leftchild,*rightchild;
int lefttag,righttag;
};
bintree
*pre;
bintree
*find(bintree *root,char in_data){
bintree
*t = root;
bintree
*node;
if(t==NULL)return NULL;
if(t->data == in_data)return t;
else{
node
= find(t->leftchild,in_data);
if(node) return node;
else return find(t->rightchild,in_data);
}
}
bintree
*createtree(){
bintree
*root = new bintree;
bintree
*parent;
string in_string;
while(cin>>in_string){
if(in_string[2]-'0'==0){
root
->data = in_string[0];
root
->leftchild = root->rightchild = NULL;
root
->lefttag = root->righttag=0;
continue;
}
parent
= find(root,in_string[2]);
if(in_string[4]-'0'==1){
bintree
*newnode = new bintree;
newnode
->data = in_string[0];
newnode
->leftchild = newnode->rightchild = NULL;
newnode
->lefttag = newnode->righttag=0;
parent
->leftchild = newnode;
}
else if(in_string[4]-'0'==2){
bintree
*newnode = new bintree;
newnode
->data = in_string[0];
newnode
->leftchild = newnode->rightchild = NULL;
newnode
->lefttag = newnode->righttag = 0;
parent
->rightchild = newnode;
}
if(getchar()=='\n')
break;
}
return root;
}

bintree
*inthreading(bintree *root){
bintree
*t = root;
if(t){
inthreading(t
->leftchild);
if(t->leftchild==NULL){
t
->lefttag = 1;
t
->leftchild = pre;
}
if(pre->rightchild==NULL){
pre
->righttag = 1;
pre
->rightchild = t;
}
pre
= t;
inthreading(t
->rightchild);
}
return root;
}
bintree
*addheadthread(bintree *root,bintree *head){
bintree
*t = root;
head
->righttag=0;
head
->rightchild = head;
if(t==NULL){
head
->lefttag = 0;
head
->leftchild = head;
}
else{
pre
= head;
head
->lefttag = 0;
head
->leftchild = t;
inthreading(t);
pre
->rightchild = head;
pre
->righttag = 1;
head
->rightchild = pre;
}
}
void inorder(bintree *head){
bintree
*t = head->leftchild;
while(t!=head){
while(t->lefttag==0)
t
= t->leftchild;
cout
<<t->data<<" ";
while(t->righttag==1&&t->rightchild!=head){
t
= t->rightchild;
cout
<<t->data<<" ";
}
t
= t->rightchild;
}
}

int main(){
bintree
*root = createtree();
bintree
*head = new bintree;
addheadthread(root,head);
inorder(head);
return 0;
}

 

 

 

7.样例输入与输出

 

输入:

A(0,0) B(A,1) C(A,2) D(B,1) E(B,2) F(C,1) G(D,1) H(D,2)

输出:

G D H B E A F C

 

本站声明
本文转载自:http://www.cnblogs.com/chengsong/p/5069454.html     作者:npusong     发布日期:2015/12/23     本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。


 
© 2014-2017 ITdaan.com 粤ICP备14056181号