二叉树遍历操作的实现以及鲁棒性的完善

本文详细介绍了如何使用先序序列构建二叉树并实现先序、中序、后序遍历。重点讨论了完善代码鲁棒性,包括判断输入序列是否能构建二叉树的whetherCan()函数,以及处理不同输入情况的策略。同时,文章通过多种输入实例展示了代码的正确性和鲁棒性。
摘要由CSDN通过智能技术生成

一、实验内容

1.实验目的

能够使用先序序列构建一棵基于二叉链表存储结构的二叉树;能够对该二叉树实现先序、中序以及后序遍历;完善鲁棒性,使得尽可能多的输入都有与之对应的输出。

2.实验内容

1)使用先序序列建一棵基于二叉链表存储结构的二叉树
使用CreatBiTree()函数构造二叉树;

2)对该二叉树实现实现先序、中序以及后序遍历
使用PreOrderTraverse()函数实现先序遍历;
使用InOrderTraverse()函数实现中序遍历;
使用PostOrderTraverse()函数实现后序遍历;

3)完善代码健壮性
使用whetherCan()函数判断所输入序列是否可以构建一棵二叉树;
whetherCan()函数流程图如下图所示:

在这里插入图片描述

4)实现代码并输出相关数据
多次输入以获得不同的输出,检验鲁棒性;

一.先给出一个例子,要构建一棵如下图的树,则需要输入的先序序列为ABC##DE#G##F###(#表示空结点,CSDN’星号‘是文本样式的一种,在代码中笔者用的是‘星号’):
在这里插入图片描述

其先序遍历结果为:A B C D E G F
其中序遍历结果为:C B E G D F A
其后序遍历结果为:C G E F D B A

二.先、中以及后序遍历的函数实现机制都大差不差,但是下面的内容中我将都做介绍;

三.鲁棒性的完善在我打草稿时候写了这么两条:
1.直接无根节点(直接回车)
2.少了必要的空节点
第一点很好判断,但在本次实验没有对其完善鲁棒性;我将在下文(二、实验过程)中的whetherCan()函数分析中着重分析第二条。

四.实验输出见下文(三、实验结果)。

二、实现过程

本次实验环境:macOS Mojave, Xcode

根据实验要求,本次实验着重分析了鲁棒性的完善,并给与较为清晰的输出。下面是实验过程:


头文件以及使用结构体以及类别名

1.头文件以及使用结构体以及类别名,stdlib头文件用于分配动态存储空间,string头文件用于调用相关函数strlen(),一个二叉树节点拥有data域以及左孩子、右孩子的指针域,如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct BiTNode {
   
   char data;
   struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

whetherCan()函数

2.判断序列是否可以构成树的whetherCan()函数,(该函数及是用来完善鲁棒性所写)以及其在main函数中的实现,如下所示:
在whetherCan()函数返回值为int类型,为的是能够在主函数中实现自身循环(当输入结果不符合要求时),传入的参数为用户输入的一段字符串。在函数体重定义并初始化了countNode以及countNull,这两个值分别表示字符串中非空(星号)符号的个数以及空符号的个数减1,为什么要减1呢?
这正是本条算法的精彩之处:通过不断地列举先序序列,笔者得出两个,假设一棵树有n个节点。那么有:
1.该树有n+1个空节点,也就是说真正的countNull(也就是空结点)的个数应当为countNode(节点个数)+1。(当然这个结论书上也有)
2.在先序序列的最后一个符号之前(易知最后一个符号一定代表空结点),从第一个字符到倒数第二个字符,countNode一定一直保持 >= countNull,该结论便是笔者多次试验得出的;
基于以上两条结论,便有了以下的判断语句,也解释了为什么countNull在最后出来只能是空结点数-1,而不能是空结点数。而whetherCan()函数在主函数中的实现也满足了要求用户输入正确序列的要求。
另外:还判断了输入字符串的长度是不是1,因为在for循环中,条件i<lens-1,如果lens==1将不会进行for循环,这种情况对应直接输入一个*或者非空字符,而这两种输入都应该判0.

//whetherCan()函数
int whetherCan(char str[]) {
   
   int countNode=0, countNull=0;
   int i;
   int lens=strlen(str);
   if(lens==1) return 0;
   for(i=0; i<lens-1; i++) {
   
      if(str[i] == '*')
         countNull += 1;
      else
         countNode += 1;
      if(countNode<countNull) return 0;
   }
   if(countNode != countNull) return 0;
   return 1;
   
//其在main函数中的实现
   char str[21];
   scanf("%s", str);
   while (!whetherCan(str)) {
   
      printf("先序序列%s无法构建一个二叉树,请重新输入:\n", str);
      scanf("%s", str);
   }
   printf("您输入的先序序列为:%s,请再次输入一遍来进行遍历:", str);
}

CreateBiTree()函数

3.创建树的CreateBiTree()函数及实现,如下所示:
可以先看下文中主函数的第一部分,可以清楚的知道:只有当用户输入正确的能够构建一棵二叉树的先序序列时,才会继续运行下面的代码,也就是说运行到CreatBiTree()函数时,用户自己已经输入了一串准确的字符串,而在主函数中要做的,就是提醒用户再对照着输入一遍,便可以准确无误地构建一棵二叉树。

//CreateBiTree()函数
int CreatBiTree(BiTree &T) {
   
   char ch;
   scanf("%c", &ch);
   if(ch=='*') T = NULL;
   else {
   
      if(!(T=(BiTNode*)malloc(sizeof(BiTNode)))) exit(-2);
      T->data = ch;
      CreatBiTree(T->lchild);
      CreatBiTree(T->rchild);
   }
   return 1;
}

//实现
   BiTree T;
   CreatBiTree(T);

Visit()函数

4.Visit()函数,如下所示:
Visit()函数用于对已经读到的结点做一个输出,充当一个功能函数。

int Visit(char  e) {
   
   printf("%c ", e);
   return 1;
}

三序算法

5.先序遍历PreOrderTraverse()函数、中序遍历InOrderTraverse()函数以及后序遍历PostOrderTraverse()函数及其实现如下所示:
PreOrderTraverse()函数:先序遍历先中后左再右,先访问“根”,再访问左子树,最后访问右子树,依次遍历;
InOrderTraverse()函数:中序遍历先左后中再右,先访问左子树,再访问“根”,最后访问右子树,依次遍历;
PostOrderTraverse()函数:后序遍历先左后右再中,先访问左子树,再访问右子树,最后访问“根”,依次遍历。
值得注意的时候在主函数用到的函数指针,这一块笔者并不是十分熟悉。

//先序
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值