数据结构二叉树运算器实验报告.docx
《数据结构二叉树运算器实验报告.docx》由会员分享,可在线阅读,更多相关《数据结构二叉树运算器实验报告.docx(21页珍藏版)》请在冰豆网上搜索。
数据结构二叉树运算器实验报告
二叉树运算器
一、问题描述
本实验主要实现对二叉树的多种方式创建、多种方式遍历、复制、销毁,求二叉树的结点数、叶子数、高度、宽度、每层宽度,用二叉树对中缀式还原,用二叉树进行求值。
二、存储结构
typedefstructBiTNode{
TElemTypedata;
structBiTNode*lchild,*rchild;
}BiNode,*BiTree;
二叉树每个结点包括数据及指向左右孩子的指针。
三、算法思想
对指针、栈、队列的综合应用。
四、程序结构
#include
#include
#include
#defineSTACK_INIT_SIZE100
#defineSTACKINCREASE10
typedefcharTElemType;
typedefstructBiTNode{
TElemTypedata;
structBiTNode*lchild,*rchild;
}BiNode,*BiTree;
typedefBiTreeSElemType;
typedefBiTreeQElemType;
typedefstructQNode{
QElemTypedata;
structQNode*next;
}QNode,*Queue;
typedefstruct{
Queuefront;
Queuerear;
}LinkQueue;
typedefstruct{
SElemType*base;
SElemType*top;
intstacksize;
}SqStack;
/*====================栈-ADT====================*/
voidInitStack(SqStack&S){
S.base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
}//InitStack
voidPush(SqStack&S,SElemTypee){
if(S.top-S.base>=S.stacksize){
S.base=(SElemType*)realloc(S.base,(S.stacksize+STACKINCREASE)*sizeof(SElemType));
if(!
S.base)
exit
(1);
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREASE;
}
*S.top++=e;
}
intPop(SqStack&S,SElemType&e){
if(S.base==S.top)
return-1;
e=*--S.top;
return0;
}
/*====================队列-ADT====================*/
voidInitQueue(LinkQueue&Q){
Q.front=Q.rear=(Queue)malloc(sizeof(QNode));
if(!
Q.front)exit
(1);
Q.front->next=NULL;
}//InitQueue
voidEnQueue(LinkQueue&Q,QElemTypee){
Queuep;
p=(Queue)malloc(sizeof(QNode));
if(!
p)exit
(1);
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
Q.rear->next=NULL;
}//EnQueue
intDeQueue(LinkQueue&Q,QElemType&e){
Queuep;
if(Q.front==Q.rear)
return0;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p){
Q.rear=Q.front;
Q.rear->next=NULL;
}
free(p);
return0;
}//DeQueue
/*====================二叉树-ADT====================*/
BiTreePreCreateBiTree(){
BiTreep;
charch;
ch=getchar();
ch=getchar();
if(ch=='#')returnNULL;
p=(BiTree)malloc(sizeof(BiNode));
p->data=ch;
p->lchild=PreCreateBiTree();
p->rchild=PreCreateBiTree();
returnp;
}//PreCreateBiTree
这是先序扩展序列创建二叉树,主要是递归调用,先赋值给双亲,再进行调用。
voidPostCreateBiTree1(BiTree&bt){
SqStackS;
charch;
BiTreep;
InitStack(S);
while
(1){
ch=getchar();
ch=getchar();
if(ch=='@')
break;
if(ch=='#')
Push(S,NULL);
else{
p=(BiTree)malloc(sizeof(BiNode));
p->data=ch;
Pop(S,p->rchild);
Pop(S,p->lchild);
Push(S,p);
}
}
Pop(S,bt);
}//PostCreateBiTree1
这是后序扩展序列创建二叉树,序列从左向右输入,以@为结束符。
扫描入栈时,每当遇到字符,则弹出两个栈顶指针最为右孩子和左孩子,直至遇到结束符@。
BiTreePostCreateBiTree2(){
charch;
BiTreep;
ch=getchar();
ch=getchar();
if(ch=='#')
returnNULL;
p=(BiTree)malloc(sizeof(BiNode));
p->data=ch;
p->rchild=PostCreateBiTree2();
p->lchild=PostCreateBiTree2();
returnp;
}//PostCreateBiTree2
这也是后序扩展序列创建二叉树,不过是从右向左输入,扫描时,每当遇到#则返回空指针。
还是用递归,先创建右孩子,再创建左孩子。
intPre_InCreate(BiTree&bt,char*P,char*Q,intn){
inti;
bt=(BiTree)malloc(sizeof(BiNode));
bt->data=P[0];
if(n==1){
bt->lchild=NULL;
bt->rchild=NULL;
return0;
}
for(i=0;iif(Q[i]==P[0])
break;
}
if(i!
=0){
bt->lchild=(BiTree)malloc(sizeof(BiNode));
bt->lchild->data=P[1];
Pre_InCreate(bt->lchild,P+1,Q,i);
}
else
bt->lchild=NULL;
if(i!
=n-1){
bt->rchild=(BiTree)malloc(sizeof(BiNode));
bt->rchild->data=P[1+i];
Pre_InCreate(bt->rchild,P+1+i,Q+1+i,n-1-i);
}
else
bt->rchild=NULL;
return0;
}//Pre_InCreate
这是用先序和中序序列创建二叉树。
主要通过分析两个序列之间的关系,通过两个序列的头尾指针和结点数,先找到双亲,再找到左右孩子,然后递归调用向下创建。
主要注意结点数的变化。
intIn_PostCreate(BiTree&bt,char*P,char*Q,intn){
inti;
bt=(BiTree)malloc(sizeof(BiNode));
bt->data=Q[n-1];
if(n==1){
bt->lchild=NULL;
bt->rchild=NULL;
return0;
}
for(i=0;iif(P[i]==Q[n-1])
break;
}
if(i!
=0){
bt->lchild=(BiTree)malloc(sizeof(BiNode));
bt->lchild->data=Q[i-1];
In_PostCreate(bt->lchild,P,Q,i);
}
else
bt->lchild=NULL;
if(i!
=n-1){
bt->rchild=(BiTree)malloc(sizeof(BiNode));
bt->rchild->data=Q[n-2];
In_PostCreate(bt->rchild,P+i+1,Q+i,n-i-1);
}
else
bt->rchild=NULL;
return0;
}//In_PostCreate
这是中序和后序序列创建二叉树。
与先序和中序一样,不再多说。
不过用先序和后序应该不能创建,因为会产生多种可能。
voidVisit(TElemTypech){
printf("%c",ch);
}//Visit
用来打印data。
intPreRead(BiTreebt){
if(!
bt)
return0;
Visit(bt->data);
PreRead(bt->lchild);
PreRead(bt->rchild);
return0;
}//PreRead
先序遍历,先将data打印,再递归调用。
intInRead(BiTreebt){
if(!
bt)
return0;
InRead(bt->lchild);
Visit(bt->data);
InRead(bt->rchild);
return0;
}//InRead
中序遍历,先递归调用左孩子,再打印data,再调用右孩子。
intPostRead(BiTreebt){
if(!
bt)
return0;
PostRead(bt->lchild);
PostRead(bt->rchild);
Visit(bt->data);
return0;
}//PostRead
后序遍历,先将左右孩子递归调用,再打印data。
intTierRead(BiTreebt){
LinkQueueQ;
BiTreep;
if(!
bt)
return0;
InitQueue(Q);
EnQueue(Q,bt);
while(!
(Q.front==Q.rear)){
DeQueue(Q,p);
Visit(p->data);
if(p->lchild)EnQueue(Q,p->lchild);
if(p->rchild)EnQueue(Q,p->rchild);
}
return0;
}//TierRead
按层遍历,运用队列,先将头指针入队,然后首队结点出队打印,每打印一个,其左右孩子入队。
直至队列为空。
intNumber(BiTreebt){
intn1,n2;
if(!
bt)return0;
n1=Number(bt->lchild);
n2=Number(bt->rchild);
return1+n1+n2;
}//Number
求二叉树结点数,递归调用,当头指针为空时,返回0,否则返回1+左孩子结点数+右孩子结点数。
intLeaf(BiTreebt){
intl1,l2;
if(!
bt)return0;
if(!
bt->lchild&&!
bt->rchild)return1;
l1=Leaf(bt->lchild);
l2=Leaf(bt->rchild);
returnl1+l2;
}//Leaf
求二叉树叶子数,递归调用,若头指针为空时,返回0,若头指针左右孩子为空,则返回1,否则返回左孩子叶子数+右孩子叶子数。
intHeight(BiTreebt){
inth1,h2;
if(!
bt)return0;
h1=Height(bt->lchild);
h2=Height(bt->rchild);
return1+(h1>h2?
h1:
h2);
}//Height
求二叉树的高度,递归调用,当头指针为空时,返回0,否则返回1+(左右孩子中高度最大的)。
intWide(BiTreebt,int*w,intx){
if(!
bt)
return0;
if(bt)
w[x]=w[x]+1;
if(bt->lchild)
Wide(bt->lchild,w,x+1);
if(bt->rchild)
Wide(bt->rchild,w,x+1);
return0;
}//Wide
求二叉树的宽度,递归调用,先求得其高度,创建一个数组输入,并输入其层数。
若双亲非空w[x]++。
调用其左右孩子。
这样就可以得到每层的宽度,找到最大值就是二叉树的宽度。
voidCopyBiTree(BiTree&P,BiTreeQ){
BiTreelp,rp;
if(!
Q)
P=NULL;
else{
CopyBiTree(lp,Q->lchild);
CopyBiTree(rp,Q->rchild);
P=(BiTree)malloc(sizeof(BiNode));
P->data=Q->data;
P->lchild=lp;
P->rchild=rp;
}
}//CopyBiTree
复制二叉树,仍是递归调用。
从下向上、从左到右创建,当头指针为空时,赋值空指针。
intDestroyBiTree(BiTree&bt){
if(!
bt)
return0;
if(bt->lchild)
DestroyBiTree(bt->lchild);
if(bt->rchild)
DestroyBiTree(bt->rchild);
if(!
bt->lchild&&!
bt->rchild){
free(bt);
bt=NULL;
return0;
}
return0;
}//DestroyBiTree
销毁二叉树,递归调用。
如果头指针为空,返回。
调用左右孩子,如果左右孩子为空,释放双亲。
开始先判断左右孩子是否非空,程序很长。
经过调整,最后判断,使函数大大缩减了。
intCalculate(BiTreebt){
intl,r;
if(!
bt->lchild&&!
bt->rchild)
returnbt->data-48;
l=Calculate(bt->lchild);
r=Calculate(bt->rchild);
switch(bt->data){
case'+':
returnl+r;
case'-':
returnl-r;
case'*':
returnl*r;
case'/':
returnl/r;
}
return0;
}//Calculate
计算二叉树的值,递归调用。
如果左右孩子为空,返回其ASCII码-48,即其实数值。
开始不是在返回时减去48,而是在计算时减去,不好操作,最后改在返回时减去48。
计算左孩子值和右孩子值,根据算数符号返回相应的值。
intRestore(BiTreebt){
if(!
bt)
return0;
if((bt->data=='*'||bt->data=='/')&&(bt->lchild->data=='+'||bt->lchild->data=='-')){
printf("(");
Restore(bt->lchild);
printf(")");
}
else{
Restore(bt->lchild);
}
Visit(bt->data);
if((bt->data=='*'||bt->data=='/')&&(bt->rchild->data=='+'||bt->rchild->data=='-')){
printf("(");
Restore(bt->rchild);
printf(")");
}
else{
Restore(bt->rchild);
}
return0;
}//Restore
还原二叉树,利用中序遍历。
如果双亲是*或者/,左孩子是+或-,则在打印左孩子两边加上()。
否则直接打印。
中间打印双亲。
右孩子同理。
/*====================main====================*/
voidmain(){
intn,m,i,j,x,y;
char*p;
char*q;
int*w;
BiTreeS[10];
for(i=0;i<10;i++)
S[i]=NULL;
printf("二叉树运算器\n");
printf("说明:
共有十个二叉树备用位置:
0~9.\n菜单:
\n0\t退出\n1\t创建二叉树\n2\t遍历二叉树\n3\t二叉树结点数\n4\t二叉树叶子数\n5\t二叉树高度\n6\t二叉树宽度\n7\t二叉树每层宽度\n8\t复制二叉树\n9\t销毁二叉树\n10\t还原中缀式\n11\t二叉树求值\n");
printf("请输入命令:
");
scanf("%d",&n);
while(n){
switch(n){
case1:
printf("1.创建方式:
\n
(1)\t扩展先序\n
(2)\t扩展后序(从左到右输入)\n(3)\t扩展后序(从右到左输入)\n(4)\t先序+中序\n(5)\t中序+后序\n请选择(");
scanf("%d",&n);
printf("(%d)请选择创建位置:
",n);
scanf("%d",&i);
while
(1){
if(i>9||i<0){
printf("位置选择错误!
请重新选择存储位置(0-9):
");
scanf("%d",&i);
}
elseif(S[i]){
printf("已占用!
请重新选择存储位置:
");
scanf("%d",&i);
}
else
break;
}
switch(n){
case1:
printf("
(1)请输入扩展先序序列:
");
S[i]=PreCreateBiTree();
break;
case2:
printf("
(2)请输入扩展后序序列(从左到右输入):
");
PostCreateBiTree1(S[i]);
break;
case3:
printf("(3)请输入扩展后序序列(从右到左输入):
");
S[i]=PostCreateBiTree2();
break;
case4:
printf("(4)请输入结点数:
");
scanf("%d",&x);
p=(char*)malloc(x*sizeof(char));
if(!
p)exit
(1);
q=(char*)malloc(x*sizeof(char));
if(!
q)exit
(1);
printf("(4)请输入先序序列:
");
for(j=0;jp[j]=getchar();
p[j]=getchar();
}
printf("(4)请输入中序序列:
");
for(j=0;jq[j]=getchar();
q[j]=getchar();
}
Pre_InCreate(S[i],p,q,x);
break;
case5:
printf("(5)请输入结点数:
");
scanf("%d",&x);
p=(char*)malloc(x*sizeof(char));
if(!
p)exit
(1);
q=(char*)malloc(x*sizeof(char));
if(!
q)exit
(1);
printf("(5)请输入中序序列:
");
for(j=0;jp[j]=getchar();
p[j]=getchar();
}
printf("(5)请输入后序序列:
");
for(j=0;jq[j]=getchar();
q[j]=getchar();
}
In_PostCreate(S[i],p,q,x);
free(p);
free(q);
break;
}
printf("1.创建完成.\n");
break;
case2:
printf("2.遍历方式:
\n
(1)\t先序遍历\n
(2)\t中序遍历\n(3)\t后序遍历\n(4)\t按层遍历\n(5)\t全部显示\n请选择(");
scanf("%d",&n);
printf("(%d)请选择遍历位置:
",n);
scanf("%d",&i);
if(!
S[i]){
printf("NULL\n");
break;
}
switch(n){
case1:
pr