这几天看了二叉树,并实际运行了一下,看起来很简单,可是真的不简单。它的实际运行过程并非是我想像的那样!我在纸上画上了一个典型的采用先序顺序建立起来的二叉树函数调用过程(采用递归实现)。下面是我的代码:
/*
① NLR:前序遍历(PreorderTraversal亦称(先序遍历))
——访问根结点的操作发生在遍历其左右子树之前。
② LNR:中序遍历(InorderTraversal)
——访问根结点的操作发生在遍历其左右子树之中(间)。
③ LRN:后序遍历(PostorderTraversal)
——访问根结点的操作发生在遍历其左右子树之后。
*/
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#define N 19
typedef struct node
{
int data;
struct node *lChild,*rChild;
}Node;
int i=-1; //控制下面函数中循环的
/*产生二叉树(利用先序递归产生)
同时一定要给一个限制范围,要不然程序
递归调用就会一直进行直到栈用完,这里
是237,并非是2M大,虽然网上很多程序
并没有说,但其实细心一点就会发现如果
没有限定范围(即让函数有一个基本解),
下面的函数中b[++i]==0并非是基本解,而
是程序的一个解而已,只不过返回的结果
为NULL而已!千万不要误解了,这是我亲身
检验过的!网上很多程序并没有指出这一点,
主要是因为结果没有受这个影响,但是这样的
程序简直就是一种浪费内存的行为*/
Node *buildTree(int b[])
{
Node *p; //创建一个根节点指针
/*N-1代表数组的最大下标,这一步的控制条件非常重要,它表示
当数组运行到最后一个元素时就返回(null是返回条件)默认最后
一个数组元素是0,必须是0,要不然无法正确建立二叉树*/
if(i>=(N-1))return NULL;
if(b[++i]==0){
p=NULL;
}else{
p=(Node*)malloc(sizeof(Node)); //开辟内存
if(!p)exit(1);
p->data=b[i]; //设置当前结点
p->lChild=buildTree(b); //左子结点
p->rChild=buildTree(b); //右子结点
} count++;
return p; //把创建的树的根结点返回
}
void preOrder(Node *root) //前序遍历
{
if(root!=NULL)
{
printf(" %d ",root->data); //打印当前结点
preOrder(root->lChild); //指向左子结点
preOrder(root->rChild); //指向右子结点
}
}
void midOrder(Node *root) //中序遍历
{
if(root!=NULL)
{
midOrder(root->lChild); //指向左子结点
printf(" %d ",root->data); //打印当前结点
midOrder(root->rChild); //指向右子结点
}
}
void lastOrder(Node *root) //后序遍历
{
if(root!=NULL)
{
lastOrder(root->lChild);
lastOrder(root->rChild);
printf(" %d ",root->data);
}
}
void main(void)
{
/*值为0的数组元素为空结点,采用先序排序一定要注由于右子
结点是最好建立的,故代表右子结点为空时函数就会返回
意!当空结点出现在右子树时表示递归结束,二叉树创建
函数就会返回到上一层直到根结点*/
int a[N]={1,2,4,0,7,0,1,0,3,5,1,0,6,0,8,1,9,0,0};
int *b=a;
/*将指向数组首地址的指针传给buildTree函数 来创建树*/
Node *root = buildTree(b);
printf("用递归方法 \n\n用前序排序:\n");
preOrder(root);
printf("用中序排序:\n");
midOrder(root);
printf("用后序排序:\n");
lastOrder(root);
getch();
}
在纸上建立二叉树的运行过程如下: