温故知新:二叉树(binary tree)的实现

树的知识点书上有很多,这儿再重新讲就显得没意思。看书时注意到,书上二叉树并没有具体提到实现,想想原因,可能是具体问题的多样性导致选着树的存储多样化。但由于缺少基本的树的实现,书上讲的各种算法难以进行比较,所以这儿给出树的实现及四种遍历方式,若有错误,欢迎指出。

首先,定义树结点,

template<typename dataType,typename elementType>
struct treeNode
{
    elementType info;
    dataType data;
    treeNode<dataType,elementType> *lchild,*rchild;
    treeNode():info(0),data(0),lchild(0),rchild(0){}
    treeNode(dataType a,elementType b):info(a),data(b),lchild(0),rchild(0){}
};

结点中的info为该结点的编号,data为存储的数据。

创建类模板tree,

template<typename dataType,typename elementType>
class Tree
{
private:
    treeNode<dataType,elementType> *root;
    int n;
public:
    Tree();
    ~Tree();
    void insertNodes(const elementType &w,const dataType &i);
    void creatTree();
    void postOrder();
    void postOrder(treeNode<dataType,elementType> *p);
    void postOrder_nonrecursion();
    void preOrder(treeNode<dataType,elementType> *p);
    void preOrder();
    void preOrder_nonrecursion();
    void inOrder(treeNode<dataType,elementType> *p);
    void inOrder();
    void inOrder_nonrecursion();
    void levelOrder();
};
构造函数
template<typename dataType,typename elementType>
Tree<dataType,elementType>::Tree():root(0),n(0){}

要注意的是root初始化只能在初始化列表中

析构函数

template<typename dataType,typename elementType>
Tree<dataType,elementType>::~Tree()
{
    struct sta{treeNode<dataType,elementType> *node;int flag;};
    sta s[n];
    int top=-1;
    treeNode<dataType,elementType> *p=root;
    while(top>-1 || p==root)
    {
        while(p!=0)
        {
            top++;
            s[top].node=p;
            s[top].flag=0;
            p=p->lchild;
        }
        while(top>-1 && s[top].flag==1)
            delete s[top--].node;
        if(top>-1)
        {
            s[top].flag=1;
            p=s[top].node->rchild;
        }
    }
}

删除结点时,采用非递归后序遍历。

在树中插入一个新的结点

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::insertNodes(const elementType &w,const dataType &i)
{
    treeNode<dataType,elementType> *pre=root;
    treeNode<dataType,elementType> *newNode=new treeNode<dataType,elementType>[1];
    newNode->info=i;
    newNode->data=w;
    newNode->lchild=0;
    newNode->rchild=0;
    if(root==0)
        root=newNode;
    else
    {
        treeNode<dataType,elementType> *p=root;
        while(p!=0)
        {
            pre=p;
            if(newNode->data>p->data)
                p=p->rchild;
            else
                p=p->lchild;
        }
        if(pre->data>newNode->data)
            pre->lchild=newNode;
        else
            pre->rchild=newNode;
    }
}

插入结点时,根据data的大小来选择插入位置,即某个结点左孩子的data一定小于等于该结点的data,右孩子的data大于该结点的data,这树称为二叉搜索树(binary search tree)。

创建树

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::creatTree()
{
    cout<<"输入树的结点个数: ";
    cin>>this->n;
    cout<<"输入各个结点信息(编号,数据),如 number data:\n";
    for(int i=0;i<this->n;i++)
    {
        dataType data;
        elementType element;
        cin>>element>>data;
        insertNodes(data,element);
    }
    cout<<"----------创建成功-----------\n";
}

在一颗空的树中不断插入结点,最后得到一颗二叉树。

后序遍历

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::postOrder()
{
    if(root==0) return;
    postOrder(root);
}
template<typename dataType,typename elementType>
void Tree<dataType,elementType>::postOrder(treeNode<dataType,elementType> *p)
{
    if(p==0) return ;
    postOrder(p->lchild);
    postOrder(p->rchild);
    cout<<p->info<<" ";
}

这儿重载了postOrder函数,需要后序遍历树时,只需调用无参的postOrder函数即可。

后序遍历(非递归)

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::postOrder_nonrecursion()
{
    struct sta{treeNode<dataType,elementType> *node;int flag;};
    sta s[n];
    int top=-1;
    treeNode<dataType,elementType> *p=root;
    while(top>-1 || p==root)
    {
        while(p!=0)
        {
            top++;
            s[top].node=p;
            s[top].flag=0;
            p=p->lchild;
        }
        while(top>-1 && s[top].flag==1)
            cout<<s[top--].node->info<<" ";
        if(top>-1)
        {
            s[top].flag=1;
            p=s[top].node->rchild;
        }
    }
}

用数组sta实现栈的功能,其中flag用来标记栈中元素的右孩子是否被访问过,当sta[top]元素的左孩子访问完,判断sta[top]->flag是否为1,不为1,则访问右孩子,否则,出栈。这样,访问顺序为:左孩子,右孩子,根结点。

注意,非递归后序遍历的sta数组所存储的元素为栈顶的结点到根结点的一条路径。

前序遍历

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::preOrder(treeNode<dataType,elementType> *p)
{
    if(p==0) return ;
    cout<<p->info<<" ";
    preOrder(p->lchild);
    preOrder(p->rchild);
}
template<typename dataType,typename elementType>
void Tree<dataType,elementType>::preOrder()
{
    if(root==0) return;
    preOrder(root);
}

前序遍历(非递归)

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::preOrder_nonrecursion()
{
    treeNode<dataType,elementType> *p=root;
    treeNode<dataType,elementType> *sta[n];
    int top=-1;
    sta[++top]=p;
    while(top>-1)
    {
        p=sta[top--];
        while(p)
        {
            cout<<p->info<<" ";
            if(p->rchild) sta[++top]=p->rchild;
            p=p->lchild;
        }
    }
}

中序遍历

template<typename dataType,typename elementType>
void Tree<dataType,elementType>::inOrder(treeNode<dataType,elementType> *p)
{
    if(p==0) return;
    inOrder(p->lchild);
    cout<<p->info<<" ";
    inOrder(p->rchild);
}
template<typename dataType,typename elementType>
void Tree<dataType,elementType>::inOrder()
{
    if(root==0) return;
    inOrder(root);
}
中序遍历(非递归)
template<typename dataType,typename elementType>
void Tree<dataType,elementType>::inOrder_nonrecursion()
{
    treeNode<dataType,elementType> *sta[n];
    int top=-1;
    treeNode<dataType,elementType> *p=root;
    while(top>-1 || p!=0)
    {
        while(p)
        {
            sta[++top]=p;
            p=p->lchild;
        }
        cout<<sta[top]->info<<" ";
        p=sta[top--]->rchild;
    }
}
层次遍历
template<typename dataType,typename elementType>
void Tree<dataType,elementType>::levelOrder()
{
    treeNode<dataType,elementType> *q[n+1];
    int maxSize=n+1;
    int front=0,rear=0;
    rear=(rear+1)%maxSize;
    q[rear]=root;
    treeNode<dataType,elementType> *p=root;
    while(rear!=front)
    {
        front=(front+1)%maxSize;
        p=q[front];
        cout<<p->info<<" ";
        if(p->lchild)
        {
            rear=(rear+1)%maxSize;
            q[rear]=p->lchild;
        }
        if(p->rchild)
        {
            rear=(rear+1)%maxSize;
            q[rear]=p->rchild;
        }
    }
}

层次遍历要用到队列,每次访问结点时,顺手把左孩子和右孩子放入队列中,由于队列是先进先出(FIFO),所以树中深度为i的结点一定会在深度为i+1的结点前面。访问完队列,就依次从左到右,从上到下访问完了树的所有结点。

对层次遍历进行修改还能得到树的最大深度和最大宽度,具体做法就是,添加一个变量last,该变量用来标记每层最后一个结点的位置:初始时,last=rear;当front==last时,last更新为last=rear。有了last变量就可以清楚地知道当前访问的是第几层和该层有多少元素。

测试

int main()
{
    Tree<float,char> r;
    r.creatTree();
    cout<<"后序遍历:\n";
    r.postOrder();
    cout<<endl;
    cout<<"非递归后序遍历:\n";
    r.postOrder_nonrecursion();
    cout<<endl;
    cout<<"前序遍历:\n";
    r.preOrder();
    cout<<endl;
    cout<<"非递归前序遍历:\n";
    r.preOrder_nonrecursion();
    cout<<endl;
    cout<<"中序遍历:\n";
    r.inOrder();
    cout<<endl;
    cout<<"非递归中序遍历;\n";
    r.inOrder_nonrecursion();
    cout<<endl;
    cout<<"层次遍历:\n";
    r.levelOrder();
    cout<<endl;
    return 0;
}

以上,便是二叉树的四种基本遍历。关于二叉树还有许多操作,但需要具体的题目或不同的树的表示方式,留待以后再记笔记。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值