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

通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数。准确的说,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
时间: 2024-08-10 18:37:30

中序线索二叉树创建及其遍历的相关文章

中序线索二叉树

虽说对于二叉树的遍历操作来说非递归法使用用户自定义的栈来代替递归使用时的系统栈,可以得到不小的效率提升,但将二叉树线索化时能将用户栈也省略掉进一步提高了效率. 对于二叉树的链表结构,n个结点的二叉树有n+1个空链域(每个叶节点都有两个空链域),而线索二叉树就把这些空链域有效的利用了起来,在一般的二叉树中,我们只知道每个结点的左右孩子,并不知道某个结点在某种遍历方式下的直接前驱和直接后继,如果能够知道前驱和后继信息,就可以把二叉树看作一个链表结构,从而可以像遍历链表那样来遍历二叉树,进而提高效率.

数据结构:中序线索二叉树

//所谓线索二叉树无非是为了让原本指向NULL的节点指向一个详细的 //已经存在的节点,逻辑上实现指针的无空指向的实现.以下是我中 //序线索二叉树的实现.还是把先序线索二叉树与后序线索分开来写吧. #include<iostream> using namespace std; template<typename Type> struct Node { Type data; bool rflags;//false为线. bool lflags; Node<Type> *

中序线索二叉树的建立

#include<stdio.h> #include<stdlib.h>//中序建立二叉树 typedef struct node { struct node *left,*right; int data; int rTag,lTag; }BNode; BNode* pre=NULL;//pre总是指向其前一个结点 BNode* CreateBTree() { int data, front=1, rear = 0; BNode *t, *root, *q[1001]; while

二叉树的中序线索化以及遍历

#include <stdio.h> #include <stdlib.h> typedef struct Node { char data; int Ltag; struct Node* LChild; int Rtag; struct Node* RChild; }BitNode, * BiTree; //先序创建二叉树 void CreatBiTree(BiTree* root) { char ch; ch = getchar(); if (ch == '.') *root

【算法与数据结构】二叉树 中序线索

中序线索二叉树 /************************************************************************ 线索二叉树 二叉树的节点有五部分构造 ----------------------------------------- | lChild | lTag | value | rTag | rChild | ----------------------------------------- lChild = (lTag == 0 ? 左

中序线索化二叉树[C语言实现及注释]

根据我自己的理解给代码加了注释. /* 中序线索二叉树 2014/11/14 */ #include<stdio.h> #include<stdlib.h> typedef struct BiTrNoDe{ char data; struct BiTrNoDe *lchild; struct BiTrNoDe *rchild; unsigned ltag : 1; //LINK是1 此处也可以用枚举类型或者宏定义去写 unsigned rtag : 1; //threading是0

线索二叉树创建及删除

题目描述 线索二叉树概念 1.定义 n个结点的二叉链表中含有n+1个空指针域.利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前趋和后继结点的指针(这种附加的指针称为"线索"). 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree).根据线索性质的不同,线索二叉树可分为前序线索二叉树.中序线索二叉树和后序线索二叉树三种. 注意: 线索链表解决了二叉链表找左.右孩子困难的问题,出现了无法直接找到该结点在某种遍历序列中的前趋和后

中序线索化二叉树

中序线索化二叉树 1 void Tree::_inTree(Node * root, Node * &pre) { 2 if (root == NULL) { // 结点为空, 1:二叉树为空 2:已到达右子树的最后一个右结点的 rchild 3 return; 4 } 5 _inTree(root->lchild, pre); // 到达当前结点的左子树的底部左结点 6 if (root->lchild == NULL) { 7 root->ltag = nChild; //

[数据结构]二叉树创建与遍历

实验报告:二叉树创建与遍历 一.问题描述 二叉树是一种实用范围很广的非线性结构,一棵非空二叉树有也只有一个根结点,每个结点最多有两个子树,我们称为左子树与右子树,当一个结点的左.右子树都是空的时,沃恩称此结点为叶子结点. 二叉树有一些很好的性质,这里不再赘述.考虑如何存储一棵树,本实验选择使用链式存储结构——二叉链表:如果事先知道需要存储的二叉树是满二叉树或者完全二叉树,则可以考虑使用顺序存储,否则将浪费大量的存储空间. 对于一棵既成的二叉树,有三种遍历方式——先序.中序与后序.可以证明,一棵形