您的位置:首页 > 其它

递归和非递归俩种方法实现二叉树的前序遍历

2012-05-22 00:00 369 查看
咱们先来复习下,基础知识。

二叉树结点存储的数据结构:

typedef char datatype;

typedef struct node

{

datatype data;

struct node* lchild,*rchild;

} bintnode;

typedef bintnode* bintree;

bintree root;

1.树的前序遍历即:

按根 左 右 的顺序,依次

前序遍历根结点->前序遍历左子树->前序遍历右子树

前序遍历,递归算法

void preorder(bintree t)

//注,bintree为一指向二叉树根结点的指针

{

if(t)

{

printf("%c",t->data);

preorder(t->lchild);

preorder(t->rchild);

}

}

然后,依葫芦画瓢,得到....

2.中序遍历,递归算法

void preorder(bintree t)

{

if(t)

{

inorder(t->lchild);

printf("%c",t->data);

inorder(t->rchild);

}

}

3.后序遍历,递归算法

void preorder(bintree t)

{

if(t)

{

postorder(t->lchild);

postorder(t->rchild);

printf("%c",t->data);

}

}

二叉树的创建方法,

void createbintree(bintree* t)

{

char ch;

if( (ch=getchar())==' ')

*t=NULL;

else

{

*t=(bintnode*) malloc(sizeof(bintnode));

(*t)->data=ch;

createbintree(&(*t)->lchild);

createbintree(&(*t)->rchild);

}

}

接下来,

咱们在讨论二叉树遍历算法的非递归实现之前,

先看一个顺序栈的定义及其部分操作的实现

typedef struct stack

{

bintree data[100];

int tag[100];

int top;

}seqstack;

void push(seqstack* s,bintree t)

{

s->data[s->top]=t;

s->top++;

}

bintree pop(seqstack* s) //出栈

{

if(s->top!=0)

{

s->top--;

return (s->data[s->top]);

}

else

return NULL;

}

好了,现在,我们可以看二叉树前序遍历的非递归实现了。

按照二叉树前序遍历的定义,无论是访问整棵树还是其子树,均应该遵循先访问根结点,

然后访问根结点的左子树,最后访问根结点的右子树的。

因为对于一棵树(子树)t,如果t非空,访问完t的根结点值后,就应该进入t的左子树,

但此时必须将t保存起来,以便访问完其左子树后,进入其右子树的访问。

yeah,就是这个意思。:)...

即在t处设置一个回溯点,并将该回溯点进栈保存。

在整个二叉树前序遍历的过程中,程序始终要做的工作分成俩个部分:

1.当前正在处理的树(子树)

2.保存在栈中等待处理的部分。

//注:当栈中元素位于栈顶即将出栈时,意味着其根结点和左子树已访问完成,

//出栈后,进入其右子树进行访问,

//前序遍历,非递归实现

void preorderT(bintree t)

{

seqstack s;

s.top=0;

while( (t)||(s.top!=0) ) //当前处理的子树不为空或栈不为空

{

while(t) //子树不为空

{

printf("%c",t->data); //1.先访问根结点

push(&s,t); //2.访问左子树之前,记得先把根结点进栈保存

t=t->lchild; //3.然后才访问左子树,

}

if(s.top>0) //栈不为空

{

t.pop(&s); //4.pop根结点

t=t->rchild; //5.访问右子树

}

}

}

//中序遍历,非递归实现,

void inorderT(bintree t)

{

seqstack s;

s.top=0;

while( (t)||(s.top!=0) ) //当前处理的子树不为空或栈不为空

{

while(t) //子树不为空

{

push(&s,t); //1.访问左子树之前,记得先把根结点push进栈

t=t->lchild; //2.访问左子树

}

if(s.top!=0) //栈不为空

{

t.pop(&s); //3.pop根结点(访问完左子树后)

printf("%c",t->data); //4.访问根结点 (把先前保存的t给拿出来,要用了..)

t=t->rchild; //5.访问右子树

}

}

}

//后序遍历,非递归实现

后序遍历的非递归算法,稍微复杂点。请看,

按照二叉树后序遍历的定义,无论是访问整棵树还是起子树,

均应该遵循先访问根结点左子树,然后访问根结点的右子树,最后访问根结点。

值得注意的是,当一个元素位于栈顶即将处理的是,其左子树的访问一定完成,

如果其右子树不为空,接下来应该进入其右子树尽情访问。

//注意了,

但此时该栈顶元素时不能出栈的,因为它作为根结点,其本身的值还未被访问。

只有等到其右子树也访问完成后,该栈顶元素才能出栈,并输出它的值。

因此,在二叉树后序遍历的算法中,必须使用seqstack类型的数组tag,

其每个元素取值为0或1,用于标识栈中每个元素的状态。

1.当一个元素刚进栈时,其对应的tag值置为0;

2.当它位于栈顶即将被处理时,其tag值为0.意味着应该访问其右子树。

于是将右子树作为当前处理的对象,此时该栈顶元素仍应该保留在栈中。

并将其对应的tag值改为1.

3.当其右子树访问完成后,该元素又一次位于栈顶,而此时其tag值为1,

意味着其右子树已访问完成,接下来,应该直接访问的就是它,将其出栈。

void postorderT(bintree t)

{

seqstack s;

s.top=0;

while( (t)||(s.top!=0) )

{

while(t)

{

s.data[s.top]=t;

s.tag[s.top]=0; //tag置为0

s.top++;

t=t->lchild; //访问左子树

}

while( (s.top>0)&&(s.tag[s.top-1]==1) )

{

s.top--;

t=s.data[s.top];

printf("%c",t->data);

}

if(s.top>0)

{

t=s.data[s.top-1];

s.tag[s.top-1]=1;

t=t->rchild;

}

else

t=NULL;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐