算法分析:构建表达树后可以发现,用栈实现逆波兰表达式其实是用后序遍历的方式来输入树的所有结点,如果用队列实现是用层序遍历的方式来输出所有结点,所以本题可以转化为已知后序遍历的序列然后构树再用层次遍历的方式输出
要构建二叉树至少知道两个遍历序列,或者设法构建扩展二叉树显然该题由小写字母就可以知道是树的根结点,可以构建成为扩展二叉树,至此,建树成功,即可层序遍历的方式输出结点
(先在输入时将遍历序列变成扩展遍历序列)
2
xyPzwIM ##x##yP##z##wIM
abcABdefgCDEF ##a##b##cAB##d##e##f##gCDEF
//然后由扩展后序遍历序列来构建二叉树,然后用层序遍历的方式保存所有结点在ans数组中,然后逆序输出ans中的所有结点
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX 10010 #define LEN sizeof(struct BTree) char string[3*MAX], ans[MAX],len; struct BTree { char s; struct BTree *rchild,*lchild; }; void input() //在输入时就构建好扩展后序遍历序列 { int i=0; char ch; while((ch=getchar())!='\n') { if(ch>='a' && ch<='z') { string[i++]='#'; string[i++]='#'; string[i++]=ch; } else string[i++]=ch; } string[i]='\0'; } void test_print_string() { int i; printf("%s\n",string); for(len=strlen(string),i=0; i<len; i++) if(string[i]!='#') printf("%c",string[i]); printf("\n"); } void create_BTree(struct BTree* *T) { len--; if(string[len]=='#') (*T)=NULL; else { (*T)=(struct BTree*)malloc(LEN); (*T)->s=string[len]; create_BTree( &((*T)->rchild) ); create_BTree( &((*T)->lchild) ); } } void travel_3(struct BTree *T) //后序遍历二叉树,只是起检验作用与本题无关 { if(T==NULL) return ; else { travel_3(T->lchild); travel_3(T->rchild); printf("%c",T->s); } } void travel_4(struct BTree *T) //层序遍历二叉树并将所有结点保存在ans数组中 { int i,n,front,rear; struct BTree *q[MAX]; i=front=rear=0; q[rear++]=T; while(front<rear) { if( q[front]->lchild ) q[rear++]=q[front]->lchild; if( q[front]->rchild ) q[rear++]=q[front]->rchild; ans[i++]=q[front]->s; //ans[i]='\0'; front++; } for(ans[i]='\0',n=strlen(ans),i=n-1; i>=0; i--) printf("%c",ans[i]); printf("\n"); } int main() { int n; struct BTree *T; scanf("%d",&n); getchar(); while(n--) { input(); //test_print_string(); len=strlen(string); create_BTree(&T); //printf("create_BTree end\n"); // travel_3(T); printf("\n"); printf("travel_3 end\n"); travel_4(T); } return 0; }
本题虽考查二叉树的相关知识,但是有一点比较特殊,就是建树的过程
建树采用扩展后序遍历序列来建树,思想是对的,但是首先要在输入时形成扩展序列,其实形成扩展序列无非是找出根结点,然后将根结点的两个孩子都赋为“#”来作为标记,但是由于本题的特殊性,可以知道,根结点仅仅可能是小写字母,且小写字母一定是根结点,所以根本不需要可以去形成一个扩展序列,只需要在建树时判断是否是小写字母,如果是,先将小写字母保存下来作为当前结点的信息,然后将它的两个孩子指针都赋为NULL,然后返回双亲结点,这样在建树过程,判断的数量没有增加反而减少(因为如果构建扩展序列,一个小写字母将产生两个#,原程序是判断#和非#,现在的程序是判断小写和大写,显然现在的程序判断次数更少,同时执行的语句也是更少的),另外省去了生成扩展序列的部分,这样速度就更大地提高了,同时也使代码变得更短
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX 10010 #define LEN sizeof(struct BTree) char string[MAX], ans[MAX],len; struct BTree { char s; struct BTree *rchild,*lchild; }; void create_BTree(struct BTree* *T) { len--; if(string[len]>='a' && string[len]<='z') { (*T)=(struct BTree*)malloc(LEN); (*T)->s=string[len]; (*T)->lchild=(*T)->rchild=NULL; return ; } else { (*T)=(struct BTree*)malloc(LEN); (*T)->s=string[len]; create_BTree( &((*T)->rchild) ); create_BTree( &((*T)->lchild) ); } } void travel_4(struct BTree *T) //层序遍历二叉树 { int i,n,front,rear; struct BTree *q[MAX]; i=front=rear=0; q[rear++]=T; while(front<rear) { if( q[front]->lchild ) q[rear++]=q[front]->lchild; if( q[front]->rchild ) q[rear++]=q[front]->rchild; ans[i++]=q[front]->s; //ans[i]='\0'; front++; } for(ans[i]='\0',n=strlen(ans),i=n-1; i>=0; i--) printf("%c",ans[i]); printf("\n"); } int main() { int n; struct BTree *T; scanf("%d",&n); getchar(); while(n--) { scanf("%s",string); len=strlen(string); create_BTree(&T); travel_4(T); } return 0; }
*************************************************************************
上面的做法思想是对的,无奈数据较大,用递归来建树会导致栈溢出,所以只好有数组结构来存放结点,
用了一个新的方法来建树,这样其实代码更短,而且似乎更好理解
#include <stdio.h> #include <string.h> #define MAX 10010 struct BTree { char s; int lchild,rchild; }tree[MAX]; int stack[MAX],top; //这个栈其实不用这么大,后来将大小换成了100,同样可以过,因为分析可知一般不会太大 int ans[MAX], front,rear; int main() { int T; char ch; int i,n,k,j; scanf("%d",&T); getchar(); while(T--) { for(i=0; (ch=getchar())!='\n'; i++) tree[i].s=ch; tree[i].s='\0'; n=i; //以上为读入数据 for(top=-1,i=0; i<n; i++) { if(tree[i].s>='a' && tree[i].s<='z') { stack[++top]=i; tree[i].lchild=tree[i].rchild=-1; } else { tree[i].rchild=stack[top]; tree[i].lchild=stack[top-1]; stack[--top]=i; } } //以上为建树 // for(i=0; i<n; i++) printf("%c %d %d \n",tree[i].s , tree[i].lchild , tree[i].rchild); memset(ans , -1 , sizeof(ans)); front=rear=0; ans[rear++]=n-1; //用了映射的思想 while(front<rear) //BFS整棵树,即层序遍历 { k=ans[front]; if(tree[k].lchild != -1) {//正规的BFS要左右孩子都要判断是否存在,不过这题特殊,判断了左孩子就可以,因为左右孩子是同时存在或同时不存在的 j=tree[k].lchild; ans[rear++]=j; j=tree[k].rchild; ans[rear++]=j; } front++; } for(i=rear-1; i>=0; i--) { k=ans[i]; printf("%c",tree[k].s); } printf("\n"); } return 0; }
//整道题的关键就在建树,在得到整棵树后要层序遍历整棵树(BFS),遍历时用了映射的思//想,其实建树的时候也用来映射的思想