非二叉树的前后序遍历
采用按层次序来建立树的孩子兄弟表示法(一个指针指向第一个孩子,一个指针指向第一个右兄弟)存储结构,实现前序和后序遍历树的操作,并编写算法求树的深度。输出的参考样张如下所示。
PS:①这个样例没有显示深度,此样例深度为4
②样例输入采用层次序输入结点对的方式。
思路:
①首先要明确,此树并非是二叉树,一个结点可能有多个兄弟节点,因此不管是遍历还是深度的计算都要包括所有的兄弟节点。
②前序遍历:根节点->前序遍历左子树->前序遍历右子树
后序遍历:后序遍历左子树->后序遍历右子树->根节点。
③由①可得,最简单的方式是采用递归的方法对树进行遍历,前序和后序唯一不同的是访问父亲结点的顺序不用。
④深度的计算同样采用递归的方法,与二叉树的深度计算不同的是,比较的是本结点深度与兄弟结点的深度哪一个更大,而不是左右子树的深度哪一个更大。
1、准备
#include <iostream>
#include<algorithm>
using namespace std;
#define max(a,b) (((a) > (b)) ? (a) : (b))
const int Max = 20; // 假定树最多有20个结点
struct TNode
{
char data; // 假定树的元素类型为char型
TNode *firstchild, *rightsib;
};
class Tree
{
public:
Tree( );
~Tree( ){Release(root);} //析构函数,释放各结点的存储空间
void PreOrder( ){PreOrder(root);}
void PostOrder( ){PostOrder(root);}
int Depth(){return Depth(root);};
private:
TNode *root;
void PreOrder(TNode *bt);
void PostOrder(TNode *bt);
void Release(TNode *bt); // 析构函数的调用
int Depth(TNode *bt);
};
Tree::Tree( )
{
TNode *Q[Max] = {NULL}; // TNode型的数组指针
char ch1 = '#', ch2 = '#';
int front = -1, rear = -1;
TNode *p = NULL, *q = NULL;
cout<<"请输入根结点:";
cin>>ch1;
p = new TNode; p->data = ch1;
p->firstchild = p->rightsib = NULL;
root = p; // 建立根结点
Q[++rear] = p; // 根结点入队
cout<<"请输入结点对,以空格分隔:";
fflush(stdin); // 清空键盘缓冲区
ch1 = getchar(); getchar(); ch2 = getchar();// 中间getchar注释掉中间的空格,ch1是父结点,ch2是孩子结点
while (ch1 != '#' || ch2 != '#') // 输入结束的条件是有序对(# #)
{
p = new TNode; p->data = ch2;
p->firstchild = p->rightsib = NULL;
Q[++rear] = p; // 入队
while (front != rear) // 当队列非空
{
q = Q[front + 1]; // 取队头元素,存储为q
if (q->data != ch1) // 当队头元素不是有序对的第一个字符
front++; // 出队
else
{
if (q->firstchild == NULL)
q->firstchild = p; // 设置结点p是结点q的第一个孩子
else
{
q = q->firstchild;
while (q->rightsib != NULL) // 查找结点q的最右兄弟
q = q->rightsib;
q->rightsib = p; // 设置结点p为结点q的右兄弟
}
break;
}
}
cout<<"请输入结点对,以空格分隔:";
fflush(stdin);
ch1 = getchar(); getchar(); ch2 = getchar();
}
}
void Tree::Release(TNode *bt)
{
if (bt == NULL) return; //递归调用的结束条件
else
{
Release(bt->firstchild); //后序递归释放bt的第一棵子树
Release(bt->rightsib); //后序递归释放bt的右兄弟子树
delete bt; //释放根结点
}
}
2、前序遍历
void Tree::PreOrder(TNode *bt)
{
if(bt==NULL)
return ;//注意这里的return含义,根节点为空或者遍历到叶子节点则本次递归结束,回溯到上一次的递归。
cout<<bt->data;//先访问根节点/父亲节点
PreOrder(bt->firstchild);//递归前序遍历本结点的第一个孩子结点
//第一个孩子结点遍历结束后则return回来再进行兄弟节点的遍历(下面一行代码)
PreOrder(bt->rightsib);//递归前序遍历本结点的第一个兄弟节点
}
3、后序遍历
void Tree::PostOrder(TNode *bt)
{
if(bt==NULL)
return ;
PostOrder(bt->firstchild);
cout<<bt->data;//后序遍历完孩子结点后,然后再访问根节点,再遍历兄弟结点(要在兄弟节点之前访问根节点)
PostOrder(bt->rightsib);
}
4、计算树的深度
int Tree::Depth(TNode *bt)
{
TNode *p;
if(bt==NULL) return 0;
if(bt->firstchild==NULL) return 1; //没有孩子结点,则返回本层的深度1
int d,max=0;
p = bt->firstchild;
while(p!=NULL){ //遍历本层所有的兄弟结点,找到深度最大的
d=Depth(p);
if(d>max)
max=d;
p = p->rightsib;
}
return max+1; //深度要加上根节点1
}