树的知识点书上有很多,这儿再重新讲就显得没意思。看书时注意到,书上二叉树并没有具体提到实现,想想原因,可能是具体问题的多样性导致选着树的存储多样化。但由于缺少基本的树的实现,书上讲的各种算法难以进行比较,所以这儿给出树的实现及四种遍历方式,若有错误,欢迎指出。
首先,定义树结点,
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;
}
![](https://i-blog.csdnimg.cn/blog_migrate/0b529905de3ad1425e08747520658cdb.png)
以上,便是二叉树的四种基本遍历。关于二叉树还有许多操作,但需要具体的题目或不同的树的表示方式,留待以后再记笔记。