最简单的树——遍历二叉树

最近因为又开学了,ACM的训练加上带低年级学弟学妹们训练,导致好久没写新东西了,趁着中秋假期终于有点时间,来再水一发基础——数据结构基础——树中最简单也是最重要的,二叉树,今天就先写一个遍历二叉树吧

什么是树呢?大概就是有根节点和子节点的一种数据结构吧,比链表更高级的是它可以有分支,而不一定是一条链的线性结构,但是也可能退化成链(后话,以后再说)直观一点就如下图的结构吧,形如此结构的数据结构叫做树:

而二叉树,就是每个节点,其子树个数小于等于2的一种树,任意一棵树均能转化为二叉树,因此二叉树可以说是整个树形数据结构研究的基本单位,后面的树上的算法也基于二叉树来写,二叉树的实例如下图:

 A称为整棵树的根,D、E、F称为叶子(因为它们没有子树),B、C及其以下的节点分别是A的左子树、右子树,可以看出不一定需要同时拥有左右子树,若一颗二叉树除叶子以外所有节点有左右子树,则我们称该二叉树为满二叉树,定义一棵树的根结点层次为1,其他节点的层次是其父结点层次加1。一棵树中所有结点的层次的最大值称为这棵树的深度,上图树深度为3,再说说完全二叉树的概念:对于深度为h的二叉树,除第h层外,其余各层节点数均达到最大值,第h层节点都连续集中在最左边,上图就是一棵完全二叉树

那么二叉树相关概念介绍了一下,什么是遍历二叉树呢?——所谓遍历二叉树,就是根据遍历的顺序不同而建立不同的二叉树,二叉树的遍历分先序遍历、中序遍历和后序遍历三种,分别是:先序遍历次序:根->左子树->右子树;中序遍历次序:左子树->根->右子树,后序遍历次序:左子树->右子树->根,以上图为例,它的三种遍历结果为:

先序遍历:A B D E C F

中序遍历:D B E A F C

后序遍历:D E B F C A

所以我们建立遍历二叉树有三种方式,分别与三种遍历方式有联系,下面我们考虑先序建树的代码要怎么写:

先序建树一定是根->左子树->右子树的顺序建立节点,而且要等到根节点所有的左子树建立完成才开始建立根的右子树,而如果根的左子节点还有左右子树,则也是按照根->左子树->右子树的顺序建立的,整个过程相当于不断重复同一个简单的规则,这不是递归的思想吗?那么递归出口是什么?当然是碰到叶子结束啦!后面无子树建立,从而使得整个递归层层返回,最后完成整个建树过程,代码如下:

typedef struct node
{
    char data;
    struct node *left,*right;
}*BTT,BTTNode;

void creatBTT(BTT &T)   //先序建树;
{
    char ch;
    cin>>ch;
    if(ch == '@')   //设置'@'为虚空节点,表示子树的结束;
    {
        T = nullptr;
        return;
    }
    T = (BTT)malloc(sizeof(BTTNode));
    T->data = ch;
    creatBTT(T->left);
    creatBTT(T->right);
}

所谓虚空节点,实际上就是一个标记符,用来表示整棵树已经达到叶子了,那么要如何遍历这棵树呢?

当然是——递归啊!无论是先序中序和后序遍历统统递归搞定,就只是调换了语句顺序,连递归出口都不改:

void preOrderTraverse(BTT T)    //先序遍历;
{
    if(!T)  return;
    cout<<T->data;
    preOrderTraverse(T->left);
    preOrderTraverse(T->right);
}

void inOrderTraverse(BTT T)     //中序遍历;
{
    if(!T)  return;
    inOrderTraverse(T->left);
    cout<<T->data;
    inOrderTraverse(T->right);
}

void postOrderTraverse(BTT T)   //后序遍历;
{
    if(!T)  return;
    postOrderTraverse(T->left);
    postOrderTraverse(T->right);
    cout<<T->data;
}

 可以看出,整个遍历算法和建立算法有几分神似,当然啦毕竟是一个原理的算法( ̄▽ ̄)

此外还可以写一些如查找树深度、统计叶子数、统计节点数、销毁遍历二叉树的函数,比较简单就不介绍了,完整程序如下:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef struct node
{
    char data;
    struct node *lc,*rc;    //左右子树;
}*TBT,TBTNode;

void creatTBT(TBT &T)   //先序建树;
{
    char ch;
    cin>>ch;
    if(ch == '@')   //设置'@'为虚空节点,表示子树的结束;
    {
        T = nullptr;
        return;
    }
    T = (TBT)malloc(sizeof(TBTNode));
    T->data = ch;
    creatTBT(T->lc);
    creatTBT(T->rc);
}

void preOrderTraverse(TBT T)    //先序遍历;
{
    if(!T)  return;
    cout<<T->data;
    preOrderTraverse(T->lc);
    preOrderTraverse(T->rc);
}

void inOrderTraverse(TBT T)     //中序遍历;
{
    if(!T)  return;
    inOrderTraverse(T->lc);
    cout<<T->data;
    inOrderTraverse(T->rc);
}

void postOrderTraverse(TBT T)   //后序遍历;
{
    if(!T)  return;
    postOrderTraverse(T->lc);
    postOrderTraverse(T->rc);
    cout<<T->data;
}

int elemNum(TBT T)   //统计节点数;
{
    if(T == nullptr)   return 0;
    return 1+elemNum(T->lc)+elemNum(T->rc);
}

int depth(TBT T)  //树深度;
{
    if(T == nullptr)    return 0;
    return max(depth(T->lc)+1,depth(T->rc)+1);    //返回较深的一侧子树深度;
}

int leafNum(TBT T)   //叶子数;
{
    if(T == nullptr)    return 0;
    if(T->lc == nullptr && T->rc == nullptr) return 1;
    return leafNum(T->lc)+leafNum(T->rc);
}

void destoryTBT(TBT &T)   //销毁遍历二叉树;
{
    if(T == nullptr)    return;
    destoryTBT(T->lc);
    destoryTBT(T->rc);
    free(T);
    T = nullptr;
}

int main()
{
    TBT T;
    cout<<"Enter the tree:"<<endl;
    creatTBT(T);
    cout<<"Succeeded!"<<endl;
    cout<<"Elem num: "<<elemNum(T)<<endl;
    cout<<"Tree depth:"<<depth(T)<<endl;
    cout<<"Leaf num: "<<leafNum(T)<<endl;
    cout<<endl<<"preOrder: ";
    preOrderTraverse(T);
    cout<<endl<<"inOrder: ";
    inOrderTraverse(T);
    cout<<endl<<"postOrder: ";
    postOrderTraverse(T);
    cout<<endl;
    destoryTBT(T);
    if(T == nullptr)
        cout<<"Tree destoryed!"<<endl;
    return 0;
}

贴一张运行结果:

嗯大概就是这样,下次找个时间写一下二叉线索树 

猜你喜欢

转载自blog.csdn.net/AAMahone/article/details/82825202
今日推荐