#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED
#define MAX 20
typedef struct tnode
{
char data;
struct tnode *lchild;
struct tnode *rchild;
int rvisit; //rvisit==1表示该节点在后序遍历时已被访问过
}TNode,*BTree;
typedef struct snode
{
int top;
int array[MAX];
}AStack;
typedef struct snode1
{
int top;
BTree pointer[MAX];
}PStack;
int seek(char ch,char *a)
{
int i;
for(i=0;i<strlen(a);i++)
{
if(a[i]==ch)
return i;
}
return 0;
}
void a_push(AStack *A,int n)
{
A->array[++(A->top)]=n;
}
void p_push(PStack *P,BTree bt)
{
P->pointer[++(P->top)]=bt;
}
BTree get(PStack *P)
{
return P->pointer[P->top];
}
void a_pop(AStack *A)
{
A->top--;
}
void p_pop(PStack *P)
{
P->top--;
}
#endif // STACK_H_INCLUDED
/*
1. 本程序中首先需定义二叉树的节点类型,节点为一个含有数据与和指针域的结构体。
2. 其次,本程序中需要用两个栈,一个用来存放指针,一个用来存放数组元素的下标。
3. 主程序中,分别输入两个字符串,作为二叉树的先序和中序序列;
两个子函数分别实现创建二叉树和后序遍历输出二叉树的功能。而在子函数中还调用了例如出栈入栈等子函数。*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stack.h"
void CreateBiTree(BTree *root,char pre[],char in[],AStack *A,PStack *P)
{
char *tag=pre;
BTree bt;
BTree q,r;
int num;
*root=(BTree)malloc(sizeof(TNode));
(*root)->data=*tag;
(*root)->lchild=NULL;
(*root)->rchild=NULL;
(*root)->rvisit=0;
bt=*root;
num=seek(*tag,in);
a_push(A,num);
p_push(P,bt);
while(*++tag!='\0')
{
num=seek(*tag,in);
bt=(BTree)malloc(sizeof(TNode));
bt->data=*tag;
bt->lchild=bt->rchild=NULL;
bt->rvisit=0;
if(num<A->array[A->top])
{
q=get(P);
q->lchild=bt;
a_push(A,num);
p_push(P,bt);
}
else if(num>A->array[A->top])
{
while(A->top!=-1&&num>A->array[A->top])
{
r=P->pointer[P->top];
p_pop(P);
a_pop(A);
}
r->rchild=bt;
a_push(A,num);
p_push(P,bt);
}
}
}
void visit(BTree p)
{
p->rvisit=1;
printf("%c",p->data);
}
void LastOrderTraverse(BTree bt,PStack *P)
{
//首先从根节点一直往左下方走,一直走到头将路径上的每一个节点入栈
BTree p,st;
p=bt;
while(bt)
{
p_push(P,bt);
bt=bt->lchild;
}
//然后进入循环体
while(P->top!=-1) //只要栈非空
{
st=get(P);//st是栈顶节点
/*注意,任意一个结点N,只要他有左孩子,则在N入栈之后,
N的左孩子必然也跟着入栈了(这个体现在算法的后半部分),
所以当我们拿到栈顶元素的时候,可以确信这个元素要么没有左孩子,
要么其左孩子已经被访问过,所以此时我们就不关心它的左孩子了,我们只关心其右孩子。*/
if(!st->rchild||st->rvisit)
{
visit(st);
p_pop(P);
}
else //若它的右孩子存在且rvisited为0,说明以前还没有动过它的右孩子,于是就去处理一下其右孩子。
{
//此时我们要从其右孩子结点开始一直往左下方走,直至走到尽头,将这条路径上的所有结点都入栈。
/*当然,入栈之前要先将该结点的rvisited设成1,因为其右孩子的入栈意味着它的右孩子必将先于它被访问(这很好理解,
因为我们总是从栈顶取出元素来进行visit)。由此可知,下一次该元素再处于栈顶时,其右孩子必然已被visit过了,所以此处可以将rvisited设置为1。*/
st->rvisit=1;
//往左下方走到尽头,将路径上所有元素入栈
p=st->rchild;
while(p)
{
p_push(P,p);
p=p->lchild;
}
}//这一轮循环已结束,刚刚入栈的那些结点我们不必管它了,下一轮循环会将这些结点照顾的很好。
}
}
int main()
{
char pre[MAX],in[MAX];
AStack A;
PStack P;
BTree root=NULL; //注意这里不能定义成BTree *root;因为这里root没有指向任何正确内存空间
A.top=-1;
P.top=-1;
printf("连续输入先序序列\n");
scanf("%s",pre);
printf("连续输入中序序列\n");
scanf("%s",in);
CreateBiTree(&root,pre,in,&A,&P);
printf("后序输出:");
P.top=-1;
LastOrderTraverse(root,&P);
return 0;
}