1.实验目的
熟练掌握二叉树的二叉链表存储结构的C语言实现。掌握二叉树的基本操作-前序、中序、后序遍历二叉树的三种方法。了解非递归遍历过程中“栈”的作用和状态,而且能灵活运用遍历算法实现二叉树的其它操作。
2.实验内容
(1)二叉树的二叉链表的创建
(2)二叉树的前、中、后序遍历的递归算法和非递归算法的实现
(3)求二叉树中叶子结点数目的递归算法。
(4)编写求二叉树深度的递归算法。
(5)编写判断二叉树是否相似的递归算法
(6)编写求二叉树左右子树互换的递归算法
(7)编写二叉树层序遍历的算法
(8)编写判断二叉树是否是完全二叉树的算法
创建二叉树的二叉链表结构、进行先、中、后序的递归函数显示不同遍历序列,结合习题了解二叉树遍历的应用实例。编写前、中、后、层序遍历的非遍历函数,注意用以前做过的栈和队列作辅助存储结构。掌握用遍历算法求解其它问题的方法。
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define TRUE 1
#define FALSE 0
#define LEN struct BiTNode
#define SIZEINIT 10
#define INCRESIZE 5
typedef int Status;
typedef char TElemType;
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild,*rchild;
}BiTNode ,*BiTree;
typedef struct SqStack{
BiTree base;
BiTree top;
int stacksize;
}SqStack;
typedef struct SqQue{
BiTree base;
int front,rear;
}SqQue;
//前序创建二叉树
Status CreatBiTree(BiTree &T){
char ch;
scanf("%c",&ch);
if(ch==' '){
T=NULL;
}else{
if(!(T = (BiTree)malloc(sizeof(LEN)))) exit(OVERFLOW);
T->data = ch;
CreatBiTree(T->lchild);
CreatBiTree(T->rchild);
}
return OK;
}
//前序遍历的递归算法
Status PreOrderTraverse(BiTree T,Status (*Visit)(TElemType e)){
if(T){
if(Visit(T->data)){
if(PreOrderTraverse(T->lchild,Visit)){
if(PreOrderTraverse(T->rchild,Visit)) return OK;
}
}
return ERROR;
}
return OK;
}
//中序遍历的递归算法
Status InOrderTraverse(BiTree T,Status (*Visit)(TElemType e)){
if(T){
if(InOrderTraverse(T->lchild,Visit)){
if(Visit(T->data)){
if(InOrderTraverse(T->rchild,Visit)) return OK;
}
}
return ERROR;
}
return OK;
}
//后序遍历的递归算法
Status PostOrderTraverse(BiTree T,Status (*Visit)(TElemType e)){
if(T){
if(PostOrderTraverse(T->lchild,Visit)){
if(PostOrderTraverse(T->rchild,Visit)){
if(Visit(T->data)) return OK;
}
}
return ERROR;
}
return OK;
}
//访问函数
Status Visit(TElemType e){
printf("%c",e);
return OK;
}
//递归算法求叶子节点的个数
int LBNode(BiTree T){
static int num = 0;
if(T){
if(T->lchild==NULL&&T->rchild==NULL){
num++;
}else{
LBNode(T->lchild);
LBNode(T->rchild);
}
}
return num;
}
//求二叉树的深度
int DepthBiTree(BiTree T){
int ldepth,rdepth;
if(T){
ldepth = DepthBiTree(T->lchild);
rdepth = DepthBiTree(T->rchild);
return (ldepth>rdepth?ldepth:rdepth)+1;
}
return 0;//如果是空树,结点数是0
}
//判断两个二叉树是否相同的递归算法
Status SimBiTree(BiTree T1,BiTree T2){
if(T1==NULL&&T2==NULL)//二者都是空树
return TRUE;
else if(T1==NULL||T2==NULL)//其中一个为空 另一个不为空
return FALSE;
else{
if(T1->data!=T2->data) return FALSE;
int l1 = SimBiTree(T1->lchild,T2->lchild);//左支树是否相似
int l2 = SimBiTree(T1->rchild,T2->rchild);//右支树是否相似
return (l1&&l2);
}
}
//左右子树交换位置的递归算法
Status change(BiTree &T){
if(T){
BiTree temp = T->lchild;
T->lchild = T->rchild;
T->rchild = temp;
change(T->lchild);
change(T->rchild);
return OK;
}
}
//初始化一个栈
Status InitStack(SqStack &s){
s.base = (BiTree)malloc(SIZEINIT*sizeof(TElemType));
if(!s.base){
printf("申请内存空间失败\n");
exit(OVERFLOW);
}
s.top = s.base;
s.stacksize = SIZEINIT;
}
//入栈
Status Push(SqStack &s,BiTNode e){
if(s.top - s.base==s.stacksize){//内存空间不够 需要重新分配
s.base = (BiTree)realloc(s.base,(s.stacksize+INCRESIZE)*sizeof(TElemType));
if(!s.base)exit(OVERFLOW);
s.top = s.base+s.stacksize;
s.stacksize += INCRESIZE;
}
*s.top++ = e;
return OK;
}
//出栈
Status Pop(SqStack &s,BiTree &p){
if(s.base==s.top){
printf("空栈!\n");
return ERROR;
}
p = --s.top;
return OK;
}
//中序遍历的非递归算法
Status InOrderTraverse2(BiTree T,Status(*Visit)(TElemType)){
if(T){
SqStack s;
BiTree p = T;//指向结点的指针
InitStack(s); //初始化一个栈用来存放树结点
while(p||s.base!=s.top){//当结点不为空 或者栈不空的时候 = 不是空树
if(p){//如果p指向结点有左子树 应先访问他的左子树
Push(s,*p);//将树的p所指结点数据入栈
p = p->lchild;
}else{//说明没有左子树 那应该访问p指向的这一结点本身
Pop(s,p);//将现在p所指结点的双亲出栈 并把他的的地址赋给p 也就是把栈顶元素返回给*p 即改变p的指向
Visit((*p).data);//没有左子树 就该访问其根结点了
p = p->rchild;//访问过根结点了 该开始遍历右子树
}
}
return OK;
}
return ERROR;
}
//初始化一个循环队列
Status InitSqQue(SqQue &q){
q.base = (BiTree)malloc(SIZEINIT*sizeof(BiTNode));
if(!q.base) exit(OVERFLOW);
q.front = q.rear = 0;
return OK;
}
//入队列
Status EnSqQue(SqQue &q,BiTree e){
if((q.rear+1)%SIZEINIT==q.front){
printf("队列已满!\n");
return ERROR;
}
*(q.base + q.rear)= *e;
q.rear = (q.rear+1)%SIZEINIT;
return OK;
}
//出队列
Status DeSqQue(SqQue &q,BiTree &p){//如果加上队列为空的判断条件的话 在层序遍历的时候会陷入死循环
p = q.base+q.front;
q.front = (q.front+1)%SIZEINIT;
}
//二叉树层序遍历的非递归算法
Status printNodeByLevel(BiTree T,Status (*Visit)(TElemType e)){
if(T){
BiTree p = T;
SqQue q;
InitSqQue(q);
EnSqQue(q,T);
while(q.front!=q.rear){//当队列不为空的时候
DeSqQue(q,p);
Visit(p->data);
if(p->lchild)
EnSqQue(q,p->lchild);
if(p->rchild)
EnSqQue(q,p->rchild);
}
}
}
//判断是否是完全二叉树
Status IsAbsoBiTree(BiTree T){
if(!T){//空树
return TRUE;
}else if(T->lchild==NULL&&T->rchild==NULL){
return TRUE;
}else{//有一个或两个孩子
SqQue q;
BiTree p = T;
InitSqQue(q);
EnSqQue(q,T);//先将根结点入队列
while(q.front!=q.rear){
DeSqQue(q,p);
if(!p->data) break;//如果出队列的数为空 就跳出这个出队入队的循环
EnSqQue(q,p->lchild);//不管有没有左右孩子 都让他们入栈
EnSqQue(q,p->rchild);
}
while(q.front!=q.rear){//判断遇到空之后 队列里还有没有非空的元素
DeSqQue(q,p);//让队列里的元素一个一个出队列判断
if(p->data) return FALSE;//当出来的元素不为空的时候 说明不是完全二叉树
}
return TRUE;//如果出来的元素全是控 也就是说队列里所剩的元素没有非空的 说明是完全二叉树
}
}
main(){
BiTree T1,T2;
int flag;
printf("\n请前序创建第一棵树(空位置用空格来代替)\n");
CreatBiTree(T1);
printf("\n0.退出");
printf("\n1.二叉树前序递归遍历的结果");
printf("\n2.二叉树中序递归遍历的结果");
printf("\n3.二叉树后序递归遍历的结果");
printf("\n4.二叉树中序非递归遍历的结果");
printf("\n5.二叉树层序遍历的结果");
printf("\n6.二叉树叶子节点的个数");
printf("\n7.二叉树二叉树的深度");
printf("\n8.判断两个二叉树是否相同");
printf("\n9.二叉树左右子树交换位置");
printf("\n10.判断是否是完全二叉树");
printf("\n请选择:");
scanf("%d",&flag);
while(flag){
switch(flag){
case 0: exit(0);
case 1: PreOrderTraverse(T1,Visit);
break;
case 2: InOrderTraverse(T1,Visit);
break;
case 3: PostOrderTraverse(T1,Visit);
break;
case 4: InOrderTraverse2(T1,Visit);
break;
case 5: printNodeByLevel(T1,Visit);
break;
case 6: printf("%d",LBNode(T1));
break;
case 7: printf("%d",DepthBiTree(T1));
break;
case 8: getchar();//消化最后输入留在缓冲区的回车键
printf("\n请创建第二棵树\n");
CreatBiTree(T2);
if(SimBiTree(T1,T2)) printf("相似\n");
else printf("不相似\n");
break;
case 9: change(T1);
PreOrderTraverse(T1,Visit);
break;
case 10:if(IsAbsoBiTree(T1)) printf("是完全二叉树");
else printf("不是完全二叉树");
break;
default:printf("输入有误 请重新输入:");
}
printf ("\n");
scanf("%d",&flag);
}
}