最近因为又开学了,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;
}
贴一张运行结果:
嗯大概就是这样,下次找个时间写一下二叉线索树