看了《大话数据结构》,手动实践,有自己的感悟,虽然看的人已经了然,但对于
实践者却意义非凡,我只是将自己的经历记录下来,不喜勿喷实践中得难点都是关于递归的,虽然其形式简单,但想理清楚层层调用关系,还是费脑力的事情。
1)实现难点:a.利用先序递归遍历的方式创建链式二叉树
b.利用递归方式求深度2)在创建二叉树时遇到的奇怪的问题
问题描述:例程在linux下可以得出结果,但只要改动输入的字符串chars,就有可能出现segmentation fault的错误
自己写出C代码后,一直出现segmentation fault的错误,直到将输入字符窜chars 改成与例程相同时,才能得出结果。分析;用户的输入要符合构成二叉树的某个条件,这个条件可能是 节点的总数,输入非空节点和空节点的数目要满足某个关系,等等。待探索!
恍然大悟:因为我们是通过前序遍历来生成二叉树的,这里说“生成”是不准确的,其实我们只是将一个已经存在的二叉树用先序遍历的方式存到内存中,
即在“生成前”已经存在这样一棵二叉树,为了通过先序遍历在内存中得到一个唯一的二叉树,我们将二叉树每个节点的空指针引出一个虚节点,
赋予特定值,本例中赋值为“#”,这种处理后的二叉树称为原二叉树的扩展二叉树。扩展二叉树就可以通过一个遍历序列确定唯一一棵二叉树。
所以,我们在输入字符序列时,其实是输入的 扩展二叉树的先序遍历序列,函数CreateBiLTree()运行成功的前提是,输入序列必须是按先序遍历
方式产生的扩展二叉树节点序列。
《大话数据结构》188页出现错误,当输入中序或后序遍历的序列时,并将代码改成相应的遍历方式,此种做法并不能生成二叉树,因为其无法生成根节点。
代码如下:(站在巨人的肩膀上)
运行环境:centos6.4 GCC 4.4.7
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /*存储空间初始分配量*/
typedef int Status;
typedef char TElemType;
typedef char String[30];/*0号单元存放数组长度*/
String StrT; //作为数据输入的数组
int pos = 1; //作为StrT的位置index,由于pos=0处存放数组长度,则此处pos=1,其在CreateBiLTree中用到
TElemType Nil = ' ';
typedef struct BiLTNode /*节点结构*/
{
TElemType data;
struct BiLTNode *lchild,*rchild;
}BiLTNode,*BiLTree;
//初始化二叉树链表,这里传入的是双重指针,为的是通过调用函数来实现对T的初始化,
//股若直接传入指针,则其必须在传入之前,即在main函数中作初始化的工作。
Status InitBiLTree(BiLTree *T)
{
*T = NULL;
return OK;
}
//访问节点,即打印出节点中的data
Status Visit(TElemType e)
{
printf("%c ",e);
return OK;
}
/*构造二叉树*/
//构造二叉树的工具函数,其会生成一个数组,作为CreateBiLTree()的输入,当然
//也可以人为输入,但是为方便对二叉树的后续操作,这里就简化了,将输入固定。
Status StrAssign(String StrT,char *s)
{
int i;
if(strlen(s)>MAXSIZE)
{
return ERROR;
}else
{
StrT[0] = strlen(s);
for(i=1;i<=StrT[0];i++)
{
StrT[i] = *(s+i-1);
}
return OK;
}
}
//创建二叉树:这里用先序遍历创建,并且此代码只能通过先序遍历方式创建,
//想通过更改else中得遍历顺序来创建中序遍历和后序遍历的二叉树是不行的,因为无法创建根节点,这是《大话数据结构》中的错误
Status CreateBiLTree(BiLTree *T)
{
if(NULL == T)
{
return ERROR;
}
TElemType ch;
ch = StrT[pos++];
if('#'==ch)
{
*T=NULL;
}else
{
*T = (BiLTree)malloc(sizeof(BiLTNode));//这里为节点分配内存,大小为struct BiLTNode的大小
if(NULL == *T)
{
exit(OVERFLOW);
}
(*T)->data = ch;
CreateBiLTree(&(*T)->lchild);
CreateBiLTree(&(*T)->rchild);
return OK;
}
}
/*构造二叉树完毕*/
//初始条件:二叉树存在
//操作结果:销毁树T
Status DestroyBiLTree(BiLTree *T)
{
if(NULL == *T)
{
return ERROR;
}
if((*T)->lchild)
{
DestroyBiLTree(&(*T)->lchild);//注意这里的&,其是(*T)-lchild变成双重指针,这样才
//符合DestroyBiLTree的参数要求
}
if((*T)->rchild)
{
DestroyBiLTree(&(*T)->rchild);//同上
}
free(*T);//释放内存空间,但*T这个变量还在.为避免*T误用,则需要*T=NULL;
//free与malloc连用,其针对的是具体的*T所指的存储空间,而
//*T=NULL针对的是指针变量
*T = NULL;
return OK;
}
//初始条件:二叉树存在
//操作结果:若T为空树,返回TRUE,否则返回FALSE
Status EmptyBiLTree(BiLTree *T)
{
if(NULL == (*T))
{
return TRUE;
}
return FALSE;
}
//初始条件:二叉树存在
//操作结果:返回树T的深度
int DepthBiLTree(BiLTree *T)
{
if(NULL == *T)
{
return 0;
}
int ldepth,rdepth;
if(!(*T)->lchild&&!(*T)->rchild)//如果左右孩子都为NULL,则只有双亲节点,则深度为1
{
return 1;
}
else//其他情况,则递归调用
{
ldepth = DepthBiLTree(&(*T)->lchild);
rdepth = DepthBiLTree(&(*T)->rchild);
return (ldepth>rdepth?ldepth:rdepth)+1;//这里要加1操作.假设深度为2,
//而走到这步说明*T有孩子,
//则至少有*T和*T的孩子,所以深度返回值为2.
//另外,ldepth和rdepth相等,则返回相等的值
}
}
//初始条件:二叉树存在
//操作结果:返回树T的根节点
TElemType Root(BiLTree *T)
{
if(EmptyBiLTree(T))
{
return Nil;
}
return (*T)->data;
}
//初始条件:二叉树存在
//操作结果:前序递归遍历
void PreOrderTraverse(BiLTree *T)
{
if(NULL == (*T))
{
return;
}
Visit((*T)->data);
PreOrderTraverse(&(*T)->lchild);
PreOrderTraverse(&(*T)->rchild);
}
//初始条件:二叉树存在
//操作结果:中序递归遍历
void InOrderTraverse(BiLTree *T)
{
if(NULL == (*T))
{
return;
}
InOrderTraverse(&(*T)->lchild);
Visit((*T)->data);
InOrderTraverse(&(*T)->rchild);
}
//初始条件:二叉树存在
//操作结果:后序递归遍历
void PostOrderTraverse(BiLTree *T)
{
if(NULL == (*T))
{
return;
}
PostOrderTraverse(&(*T)->lchild);
PostOrderTraverse(&(*T)->rchild);
Visit((*T)->data);
}
int main()
{
char *chars = "ABCD#K###E##CFI###G#J##";//全局变量
StrAssign(StrT,chars);
BiLTree *T;//T是双重指针
T = (BiLTree *)malloc(sizeof(BiLTree));
InitBiLTree(T);
CreateBiLTree(T);
printf("构造二叉树后,二叉树是否为空(1:是,0:不是)? %d\n",EmptyBiLTree(T));
printf("二叉树的深度为 %d\n",DepthBiLTree(T));
printf("二叉树的根为:%c\n",Root(T));
printf("前序递归遍历:");
PreOrderTraverse(T);
printf("\n");
printf("中序递归遍历:");
InOrderTraverse(T);
printf("\n");
printf("后序递归遍历:");
PostOrderTraverse(T);
printf("\n");
//销毁树
DestoryBiLTree(T);
printf("构造二叉树后,二叉树是否为空(1:是,0:不是)? %d\n",EmptyBiLTree(T));
}