2:树的镜面映射
-
总时间限制:
- 1000ms 内存限制:
- 65536kB
-
描述
-
一棵树的镜面映射指的是对于树中的每个结点,都将其子结点反序。例如,对左边的树,镜面映射后变成右边这棵树。
a a / | \ / | \ b c f ===> f c b / \ / \ d e e d
我们在输入输出一棵树的时候,常常会把树转换成对应的二叉树,而且对该二叉树中只有单个子结点的分支结点补充一个虚子结点“$”,形成“伪满二叉树”。
例如,对下图左边的树,得到下图右边的伪满二叉树
a a / | \ / \ b c f ===> b $ / \ / \ d e $ c / \ d f / \ $ e
然后对这棵二叉树进行前序遍历,如果是内部结点则标记为0,如果是叶结点则标记为1,而且虚结点也输出。
现在我们将一棵树以“伪满二叉树”的形式输入,要求输出这棵树的镜面映射的宽度优先遍历序列。
输入
-
输入包含一棵树所形成的“伪满二叉树”的前序遍历。
第一行包含一个整数,表示结点的数目。
第二行包含所有结点。每个结点用两个字符表示,第一个字符表示结点的编号,第二个字符表示该结点为内部结点还是外部结点,内部结点为0,外部结点为1。结点之间用一个空格隔开。
数据保证所有结点的编号都为一个小写字母。
输出
- 输出包含这棵树的镜面映射的宽度优先遍历序列,只需要输出每个结点的编号,编号之间用一个空格隔开。 样例输入
-
9 a0 b0 $1 c0 d0 $1 e1 f1 $1
样例输出
-
a f c b e d
提示
- 样例输入输出对应着题目描述中的那棵树。
-
- ============================================================
- 这道题目我在做dsalgo@openjudge的时候是木有的,今天在某同学的提醒下才发现,那就来试试吧,反正每天dp入门也快吐了- -
-
-
解决过程分为两个部分,
建树和
打印镜像。
- 建树部分。给出的序列是先根遍历,树是二叉树。设置一个根结点栈,栈顶结点就是当前的父亲。具体而言,若栈顶没有左儿子,则当前结点是它的左儿子;若栈顶有左儿子没有右儿子,则当前结点是它的右儿子;若栈顶两个儿子都有,那么当前结点就不是它的儿子(是栈顶的兄弟或者旁系祖先),这时需要不断退栈,找当前结点的父亲,也就是没有右儿子的那个。还有一点,若当前结点是内部结点,它必然是父亲,也必然要入栈;若是外部结点,它只是儿子,所以无需入栈。
- 打印部分。还是bf traversal的框架,主要是镜像问题。其实也好办,把儿子们从右向左倒着入队就可以了(同样借助栈来完成)。
更多细节请看详细注释的代码。============================================================#include <stdio.h> #define MAXNODE 1024 char NodeInfoSeq[MAXNODE*2]; //输入信息序列,格式为...(信息,内/外部结点)(信息,内/外部结点)... char OutputDump[MAXNODE]; typedef struct fake_bt_node { char info; struct fake_bt_node *lc; struct fake_bt_node *rc; } fbtn; fbtn* nStack[MAXNODE]; fbtn* nQueue[MAXNODE*2]; //贪图省事,没有使用环形队列...front可能推到很后很后...所以队列空间开大一点 fbtn *BuildFakeBinaryTree(int n) { int sp=-1; //栈指针sp;栈空时为-1。栈顶为当前的父结点。 //先建立树根结点 fbtn *root=new fbtn; root->info=NodeInfoSeq[0]; root->lc=NULL; root->rc=NULL; //树根结点进栈 nStack[++sp]=root; for (int i=2; i<2*n; i+=2) { //根据输入序列新建一个结点 fbtn *temp=new fbtn; temp->info=NodeInfoSeq[i]; temp->lc=NULL; temp->rc=NULL; //接下来,把这个新结点连在伪满二叉树上 fbtn *curFa=nStack[sp]; //栈顶结点是当前结点的父亲 if (curFa->lc==NULL) //当前结点是左儿子 curFa->lc=temp; else if (curFa->lc!=NULL && curFa->rc==NULL) //当前结点是右儿子 curFa->rc=temp; else //curFa的左、右儿子都有,说明curFa不是当前结点的父亲结点,那就退栈,找当前结点的父亲结点,且当前结点一定是右儿子 { while (nStack[sp]->rc!=NULL) --sp; nStack[sp]->rc=temp; } //接下来,这个新结点是否要进栈呢? if (NodeInfoSeq[i+1]=='0') //如果它是内部结点,那么它一定是父亲,要进栈 nStack[++sp]=temp; } return root; } void PrintMirrorImage(fbtn *root) { int qf=0, qr=-1; //初始时队列为空;队列为空的标志是:rear+1==front int sp=-1; int dp=-1; //OutputDump的指针 fbtn* head; //enqueue root nQueue[++qr]=root; //bf traversal框架 while (qr+1!=qf) //队列非空 { //dequeue head head=nQueue[qf++]; //先提取head的信息 OutputDump[++dp]=head->info; //再把head的所有儿子都入队...但由于要输出镜像,所以要反向入队;我们借助栈来完成。 if (head->lc!=NULL) //head是有儿子的 { fbtn *cc=head->lc; //cc means current child while (cc!=NULL) //所有儿子全入栈! { nStack[++sp]=cc; cc=cc->rc; } while (sp!=-1) //出栈,入队...这样就是镜像的 nQueue[++qr]=nStack[sp--]; } } //打印输出序列 for (int i=0; i<=dp; ++i) { if (OutputDump[i]!='$') printf("%c ", OutputDump[i]); } } //不要忘记释放树的内存 void DeleteFakeBinaryTree(fbtn *root) { if (root!=NULL) { DeleteFakeBinaryTree(root->lc); DeleteFakeBinaryTree(root->rc); delete root; root=NULL; } } int main() { freopen("D:\\in.txt", "r", stdin); freopen("D:\\out.txt", "w", stdout); int n, i=0; char c; scanf("%d", &n); getchar(); //取走数字n后面的换行符 while ((c=getchar())!='\n') { if(c!=' ') NodeInfoSeq[i++]=c; } fbtn *root=BuildFakeBinaryTree(n); PrintMirrorImage(root); DeleteFakeBinaryTree(root); return 0; }