一、实验名称:二叉树
二、实验目的
1)熟练掌握二叉树的存储方式的具体实现过程,实现二叉树的基本操作及运算;
2)进一步巩固指针的用法,栈及队列的基本操作,进一步体会递归算法,学会综合应用。
三、实验要求
- 使用链式存储结构实现二叉树的存储;
- 运用递归及非递归实现二叉树的三种顺序遍历,及层次遍历;
- 运用递归实现二叉树中结点,深度的计算和二叉树的复制相关操作;
- 将所有相关操作整合成一个选择系统,以方便调用及实现。
四、实验环境
(1) 硬件环境:微机一台;
(2) 软件环境:windows7+VC++ 2010/6.0集成开发环境。
五、实验内容及测试分析
1)编程思想
1.定义二叉树的二叉链表存储形式:包括结点数据域,左右孩子指针, 其中运用了递归操作。
2.按照先序遍历的顺序递归创建二叉链表:递归出口:输入为#;
否则,构建一个新的树结点,结点数据域为ch,再调用自 身分别创建结点的左子树,右子树。
3.递归实现先序遍历:递归的出口:树为空:
否则,访问根结点,再调用自身遍历左子树及右子树;
中序,后序,双序遍历的递归算法,在先序遍历的基础上改变访问根结点 的顺序即可。
4.非递归实现先序遍历:利用栈先进后出的思想,
初始化一个空栈S,指针p指向根结点;
申请结点空间q,存放栈顶弹出的元素;
当p非空或栈S非空时,执行以下循环:
如果p非空,则访问根结点,p进栈,p指向该结点左孩 子;
如果p为空,则弹出栈顶元素,将p指向该结点右子树。
5.非递归实现中序遍历:跟先序遍历算法相似,只需要在p为空时弹 出栈顶元素并访问根结点。
6.队列实现层次遍历:利用队列先进先出的特性;
初始化一个空队列Q,指针p指向根结点,根结点入队;
申请结点空间q,存放队列中出队的元素;
当队列Q非空时,执行以下循环:
队尾元素出队;访问根结点;
如果该结点的左子树非空,左子树入队;
如果该结点的右子树非空,右子树入队。
7.递归实现二叉树的复制:如果是空树,递归结束,否则执行以下操 作:
申请一个新结点空间,复制根结点;
递归复制左子树;
递归复制右子树。
8.递归求二叉树的深度:如果是空树,递归结束,深度为0,否则执 行以下操作:
递归计算左子树的深度记为m;
递归计算右子树的深度记为n;
如果m>n,二叉树的深度为m+1,否则为n+1。
9.递归求结点个数:如果是空树,则结点个数为0;否则,结点个数为 左子树的结点个数加上右子树的结点个数;
同理可求度为1,度为2的结点个数,及叶子节点个数,只 需要加一个if语句,判断是否符合即可。
10.递归实现交换左右子树:如果是空树,递归结束;否则执行以下操 作:
申请一个新结点空间t,t指向根结点的左子树;
将根结点的右子指向根结点的左子树;
根结点的左子树指向t;
递归交换结点的左子树;
递归交换结点的右子树。
11.上述算法实现以后,利用while语句做一个选择菜单界面,实现输入 数字执行相应操作。
2)程序代码
#include <iostream>
#define MAXSIZE 100
using namespace std;
typedef struct BiTNode{ //二叉树的存储结构
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
typedef struct //栈的存储结构
{
BiTree *base;
BiTree *top;
int stacksize;
} SqStack;
void InitStack(SqStack &S) //构造一个空栈
{
S.base=new BiTree[MAXSIZE];
if(!S.base) exit(-2);
S.top=S.base;
S.stacksize=MAXSIZE;
}
typedef struct{ //队列的存储结构
BiTree *base; //存储空间的基指针
int front; //头指针(伪指针)
int rear; //尾指针(伪指针)
}SqQueue;
//循环队列初始化
int InitQueue(SqQueue &Q)
{ //构造一个空队列Q
Q.base=new BiTree[MAXSIZE]; //为队列分配一个最大容量为MAXSIZE的数组空间
if(!Q.base) exit(-2); //存储分配失败
Q.front=Q.rear=0; //头指针和尾指针置为0,队列为空
return 1;
}
//循环队列判空
bool QueueEmpty(SqQueue Q)
{
if(Q.front==Q.rear) return true;
else return false;
}
// 循环队列入队
int EnQueue(SqQueue &Q,BiTree e)
{
if((Q.rear+1)%MAXSIZE==Q.front)
return 0;
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXSIZE;
return 1;
}
//循环队列的出队
int DeQueue(SqQueue &Q,BiTree &e)
{
if(Q.front==Q.rear) return 0;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXSIZE;
return 1;
}
//取队头元素
BiTree GetHead(SqQueue Q)
{
if(Q.front!=Q.rear)
return Q.base[Q.front];
}
bool StackEmpty(SqStack S)//判断栈是否为空
{
if(S.top==S.base) return true;
else return false;
}
int Push(SqStack &S,BiTree e) //入栈
{
if(S.top-S.base==S.stacksize)return -1;
*(S.top++)=e;
return 1;
}
int Pop(SqStack &S,BiTree &e) //出栈
{
if(S.top==S.base) return 0;
e= *(--S.top);
return 1;
}
void createBiTree(BiTree &T){ //先序遍历创建二叉树 递归实现
char ch;
cin>>ch;
if(ch=='#')T=NULL; //递归出口
else{
T=new BiTNode;
T->data=ch;
createBiTree(T->lchild); //创建左子树
createBiTree(T->rchild); //创建右子树
}
}
void FirstOrderTraverse(BiTree T){ //先序遍历二叉树,递归实现
if(T){
cout<<T->data;
FirstOrderTraverse(T->lchild);
FirstOrderTraverse(T->rchild);
}
}
void FirstOrderTraverse_1(BiTree T){ //先序遍历二叉树,非递归实现
SqStack S;
InitStack(S);
BiTree p,q;
p=T;
q=new BiTNode;
while(p||!StackEmpty(S)){
if(p){
cout<<p->data;
Push(S,p);
p=p->lchild;
}
else {
Pop(S,q);
p=q->rchild;
}
}
}
void InOrderTraverse(BiTree T){ //中序遍历二叉树,递归实现
if(T){
InOrderTraverse(T->lchild);
cout<<T->data;
InOrderTraverse(T->rchild);
}
}
void InOrderTraverse_1(BiTree T) { //中序遍历二叉树,非递归实现
SqStack S;
InitStack(S);
BiTree p,q;
p=T;
q=new BiTNode;
while(p||!StackEmpty(S)){
if(p){
Push(S,p);
p=p->lchild;
}
else {
Pop(S,q);
cout<<q->data;
p=q->rchild;
}
}
}
void LastOrderTraverse(BiTree T){ //后序遍历二叉树,递归实现
if(T){
LastOrderTraverse(T->lchild);
LastOrderTraverse(T->rchild);
cout<<T->data;
}
}
void OrderTraverse(BiTree T){ //层次遍历二叉树,队列实现
SqQueue Q;
InitQueue(Q);
EnQueue(Q,T);
BiTree p,q;
p=T;
q=new BiTNode;
while(!QueueEmpty(Q)){
DeQueue(Q,q);
cout<<q->data;
if(q->lchild!=NULL)EnQueue(Q,q->lchild);
if(q->rchild!=NULL)EnQueue(Q,q->rchild);
}
}
void DoubleOrderTraverse(BiTree T) { //双序遍历二叉树,递归实现
if(T){
cout<<T->data;
DoubleOrderTraverse(T->lchild);
cout<<T->data;
DoubleOrderTraverse(T->rchild);
}
}
void Copy(BiTree T,BiTree &NewT){ //复制二叉树
if(T==NULL){
NewT=NULL;
return ;
}
else{
NewT=new BiTNode;
NewT->data=T->data;
Copy(T->lchild,NewT->lchild);
Copy(T->rchild,NewT->rchild);
}
}
int Depth(BiTree T){ //计算二叉树的深度
int m,n;
if(T==NULL) return 0;
else{
m=Depth(T->lchild);
n=Depth(T->rchild);
if(m>n) return(m+1);
else return(n+1);
}
}
int NodeCount(BiTree T){ //统计结点个数
if(T==NULL) return 0;
else{
return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
}
}
int NodeCount_0(BiTree T){ //统计叶子结点个数
if(T==NULL)return 0;
else if(T->lchild==NULL&&T->rchild==NULL)
return NodeCount_0(T->lchild)+NodeCount_0(T->rchild)+1;
else return NodeCount_0(T->lchild)+NodeCount_0(T->rchild);
}
int NodeCount_1(BiTree T){ //统计度为1的结点个数
if(T==NULL)return 0;
else if((T->lchild==NULL&&T->rchild!=NULL)||(T->lchild!=NULL&&T->rchild==NULL))
return NodeCount_1(T->lchild)+NodeCount_1(T->rchild)+1;
else return NodeCount_1(T->lchild)+NodeCount_1(T->rchild);
}
int NodeCount_2(BiTree T){ //统计度为2的结点个数
if(T==NULL)return 0;
else if(T->lchild!=NULL&&T->rchild!=NULL)
return NodeCount_2(T->lchild)+NodeCount_2(T->rchild)+1;
else return NodeCount_2(T->lchild)+NodeCount_2(T->rchild);
}
void changeChild(BiTree &T){ //交换左右子树
BiTree t;
if(T==NULL) return ;
else {
t=new BiTNode;
t=T->lchild;
T->lchild=T->rchild;
T->rchild=t;
changeChild(T->lchild);
changeChild(T->rchild);
}
}
void menu(){ //菜单界面
cout<<"请选择操作:"<<endl;
cout<<" 1.创建二叉树"<<endl;
cout<<" 2.先序遍历二叉树"<<endl;
cout<<" 3.中序遍历二叉树"<<endl;
cout<<" 4.后序遍历二叉树"<<endl;
cout<<" 5.层序遍历二叉树"<<endl;
cout<<" 6.双序遍历二叉树"<<endl;
cout<<" 7.复制二叉树"<<endl;
cout<<" 8.计算二叉树深度"<<endl;
cout<<" 9.统计结点个数"<<endl;
cout<<" 10.统计叶子结点个数"<<endl;
cout<<" 11.统计度为1的结点个数"<<endl;
cout<<" 12.统计度为2的结点个数"<<endl;
cout<<" 13.交换左右子树"<<endl;
cout<<" 14.退出"<<endl;
}
int main()
{
BiTree T;
int choose;
menu();
while(cin>>choose&&choose!=14){
switch(choose){
case 1:{
cout<<"请按照先序遍历的顺序输入一个二叉树(空树以#标记):";
createBiTree(T);
cout<<"二叉树创建成功!"<<endl;
break;
}
case 2:{
cout<<"先序遍历的结果为:"<<endl;
FirstOrderTraverse(T);
cout<<endl;
FirstOrderTraverse_1(T);
cout<<endl;
break;
}
case 3:{
cout<<"中序遍历的结果为:"<<endl;
InOrderTraverse(T);
cout<<endl;
InOrderTraverse_1(T);
cout<<endl;
break;
}
case 4:{
cout<<"后序遍历的结果为:"<<endl;
LastOrderTraverse(T);
cout<<endl;
break;
}
case 5:{
cout<<"层次遍历的结果为:"<<endl;
OrderTraverse(T);
cout<<endl;
break;
}
case 6:{
cout<<"双序遍历的结果为:"<<endl;
DoubleOrderTraverse(T);
cout<<endl;
break;
}
case 7:{
BiTree NewT;
Copy(T,NewT);
cout<<"复制完毕!"<<endl;
cout<<"复制后的二叉树先序遍历为:"<<endl;
FirstOrderTraverse(NewT);
cout<<endl;
break;
}
case 8:{
cout<<"二叉树的深度为:";
cout<<Depth(T);
cout<<endl;
break;
}
case 9:{
cout<<"二叉树中结点个数为:";
cout<<NodeCount(T);
cout<<endl;
break;
}
case 10:{
cout<<"二叉树中叶子结点个数为:";
cout<<NodeCount_0(T);
cout<<endl;
break;
}
case 11:{
cout<<"二叉树中度为1的结点个数为:";
cout<<NodeCount_1(T);
cout<<endl;
break;
}
case 12:{
cout<<"二叉树中度为2的结点个数为:";
cout<<NodeCount_2(T);
cout<<endl;
break;
}
case 13:{
cout<<"已交换左右子树";
changeChild(T);
cout<<endl;
break;
}
default:{
cout<<"输入有误,请重新输入"<<endl;
break;
}
}
menu();
}
}
输入数据:ABD##E##CF###
输出结果:2.先序遍历的结果为:
ABDECF
ABDECF
3.中序遍历的结果为:
DBEAFC
DBEAFC
4.后序遍历的结果为:
DEBFCA
5.层次遍历的结果为:
ABCDEF
6.双序遍历的结果为:
ABDDBEEACFFC
7.复制完毕!
复制后的二叉树先序遍历为:
ABDECF
8.二叉树的深度为:3
9.二叉树中结点个数为:6
10.二叉树中叶子结点个数为:3
11.二叉树中度为1的结点个数为:1
12.二叉树中度为2的结点个数为:2
13.已交换左右子树
14.退出
5)出现的问题及解决方法
注意改变栈及队列的指针数据类型。
六、实验总结
(1).树本身是一种非线性结构,在二叉树的基本操作的算法中多次利用到递归思想,二叉树本身的定义即为递归定义,调用自身定义左右孩子指针;后继的遍历等算法过程中也多次利用到递归;
(2).三种遍历算法的递归算法不同之处仅在于访问根结点和遍历左、右子树的先后关系,需要注意的是,任何一棵二叉树的叶子结点在先序,中序,后序遍历中,其访问的相对次序不变,包括双序遍历仅是在先序遍历的基础上,在访问右子树之前再次访问根结点;
(3).利用栈实现先序,中序遍历时,需要注意在定义栈的存储结构时,其头指针,尾指针的数据类型为指向根结点的指针的指针,队列实现层序遍历同理;
(4).本次实验是对之前所学栈,队列,递归的具体实现及综合应用,熟练掌握栈先进后出,队列先进先出的特性可以解决很多类似问题。