实验五 二叉树的递归及非递归的遍历及其应用

  1. 实验目的

熟练掌握二叉树的二叉链表存储结构的C语言实现。掌握二叉树的基本操作-前序、中序、后序遍历二叉树的三种方法。了解非递归遍历过程中“栈”的作用和状态,而且能灵活运用遍历算法实现二叉树的其它操作。

  1. 实验内容

(1)二叉树的二叉链表的创建

(2)二叉树的前、中、后序遍历的递归算法和非递归算法的实现

(3)求二叉树中叶子结点数目的递归算法。

(4)编写求二叉树深度的递归算法。

(5)编写判断二叉树是否相似的递归算法

(6)编写求二叉树左右子树互换的递归算法

(7)编写二叉树层序遍历的算法

(8)编写判断二叉树是否是完全二叉树的算法

创建二叉树的二叉链表结构、进行先、中、后序的递归函数显示不同遍历序列,结合习题了解二叉树遍历的应用实例。编写前、中、后、层序遍历的非遍历函数,注意用以前做过的栈和队列作辅助存储结构。掌握用遍历算法求解其它问题的方法。

  1. 问题描述

    (说明你选做的题目及要求

二叉树的创建,前、中、后序遍历的递归算法和非递归算法,求叶子结点数目,求二叉树深度,判断二叉树是否相似,求二叉树左右子树互换,二叉树层序遍历的算法,判断二叉树是否是完全二叉树,要求用c语言实现,熟练掌握二叉链表存储结构,谅解非递归遍历过程的栈的应用,灵活运用二叉树的其他操作。

  1. 数据结构定义

    (说明你算法中用到的数据结构、数据类型的定义

树的定义,树是由n个结点组成的有限集合。其中,如果n=0,他是一棵空树,这是树的特例。如果n》0,这n个结点中存在一个结点作为树的根节点,简称为根(root),其余结点可分为m个互不相交的有限集,其中每一棵子集本身又是一课符合本定义的树,称为根root的子树。

二叉树是另一种树形结构,它的特点是每个结点至多只有两棵子树,并且二叉树的子树有左右之分,其次序不能任意颠倒。

存储结构:顺序存储#define MAX_TREE_SIZE  100

typedef TElemTYpe SqBiTree[MAX_TREE_SIZE];

SqBiTree  bt;

链式存储:

typedef struct BiTNode{

TElemType  data;

struct BiTNode *lchile ,*rchild;

}BiTNode, * BiTree;

  1. 算法思想及算法设计

    (先文字说明算法的思想,然后给出类C语言算法

前序遍历:前序遍历顺序为:规则是先是根结点,再前序遍历左子树,再前序遍历右子树

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;

}

 求叶子节点的个数:我们知道求所有叶子节点的方法,是递归的将左右子树的叶子节点值相加。

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;}

求二叉树最大深度:从根节点开始,最大深度为 Math.max(左子树的最大深度,右子树的最大深度比较)+ 1;左子树的最大深度, 仍然是相对于左子树的根节点来说Math.max(左子树的最大深度,和右子树的最大深度比较)+ 1,是的,递归。但是和上面比起来,是自下而上。

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

}
  1. 实验代码

    (即C语言程序

#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);

}

}

  1. 算法测试结果

    (说明测试数据,粘贴实验结果图

 

  1. 分析与总结

    (1)算法复杂度分析及优、缺点分析

        (说明你编写算法的复杂度,算法的优点和缺点有哪些

二叉树的遍历中由于每个节点都要进栈和出栈,同样的二叉树的递归和非递归遍历都是如初,所以时间复杂度O(N),同样空间复杂度也为O(N),N为结点数。

求二叉树最大深度的算法中,对于一个具有n个节点的二叉树,需要调用函数2n+1次访问其扩充二叉树全部结点,尾递归方式开辟临时空间n个用于变量存储,时间复杂度O(n)空间复杂度O(n)

    (2)实验总结

        (说明你怎么解决实验中遇到的问题,有什么收获

通过这次试验,让我熟练掌握二叉树的二叉链表存储结构的C语言实现。掌握二叉树的基本操作-前序、中序、后序遍历二叉树的三种方法。了解非递归遍历过程中“栈”的作用和状态,而且能灵活运用遍历算法实现二叉树的其它操作。也让我知道了如何解决实际操作过程中遇到的困难,通过向同学老师求助,网上资料的查询。

  • 10
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值