您的位置:首页 > 其它

USACO section 3.4 American Heritage(二叉树序列)

2013-03-07 15:58 447 查看
American Heritage

Farmer John takes the heritage of his cows very seriously. He is not, however, a truly fine bookkeeper. He keeps his cow genealogies as binary trees and, instead of writing them in graphic form, he records them
in the more linear `tree in-order' and `tree pre-order' notations.

Your job is to create the `tree post-order' notation of a cow's heritage after being given the in-order and pre-order notations. Each cow name is encoded as a unique letter. (You may already know that you can frequently
reconstruct a tree from any two of the ordered traversals.) Obviously, the trees will have no more than 26 nodes.

Here is a graphical representation of the tree used in the sample input and output:

C
/   \
/     \
B       G
/ \     /
A   D   H
/ \
E   F

The in-order traversal of this tree prints the left sub-tree, the root, and the right sub-tree.

The pre-order traversal of this tree prints the root, the left sub-tree, and the right sub-tree.

The post-order traversal of this tree print the left sub-tree, the right sub-tree, and the root.

PROGRAM NAME: heritage

INPUT FORMAT

Line 1:The in-order representation of a tree.
Line 2:The pre-order representation of that same tree.

SAMPLE INPUT (file heritage.in)

ABEDFCHG
CBADEFGH

OUTPUT FORMAT

A single line with the post-order representation of the tree.

SAMPLE OUTPUT (file heritage.out)

AEFDBHGC

思路:一、已知二叉树的前序序列和中序序列,求解树。

1、确定树的根节点。树根是当前树中所有元素在前序遍历中最先出现的元素。

2、求解树的子树。找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树。若根节点左边或右边为空,则该方向子树为空;若根节点左边和右边都为空,则根节点已经为叶子节点。

3、递归求解树。将左子树和右子树分别看成一棵二叉树,重复1、2、3步,直到所有的节点完成定位。

/*
ID:nealgav1
LANG:C++
PROG:heritage
*/
#include<fstream>
#include<cstring>
using namespace std;
const int mm=55;
ifstream cin("heritage.in");
ofstream cout("heritage.out");
class node
{
public:char v;
node*l,*r;
node(){l=r=NULL;}
}*pn;
char pre[mm],mid[mm],post[mm];
///i为在pre中的位置,j为在mid的开始端,len为长度
void creattree(node*&p,int i,int j,int len)
{ if(len<=0)return;
p=new node();
p->v=pre[i];
int m=strchr(mid,pre[i])-mid;
creattree(p->l,i+1,j,m-j);
creattree(p->r,i+m-j+1,m+1,len-(m-j)-1);
}
void postvisit(node*p)
{
if(p!=NULL)
{
postvisit(p->l);
postvisit(p->r);
cout<<p->v;
}
}
int main()
{
while(cin>>mid>>pre)
{ pn=NULL;
creattree(pn,0,0,strlen(mid));
postvisit(pn);
cout<<"\n";
}
}


USER: Neal Gavin Gavin [nealgav1]
TASK: heritage
LANG: C++

Compiling...
Compile: OK

Executing...
Test 1: TEST OK [0.000 secs, 3364 KB]
Test 2: TEST OK [0.000 secs, 3364 KB]
Test 3: TEST OK [0.000 secs, 3364 KB]
Test 4: TEST OK [0.000 secs, 3364 KB]
Test 5: TEST OK [0.000 secs, 3364 KB]
Test 6: TEST OK [0.000 secs, 3364 KB]
Test 7: TEST OK [0.000 secs, 3364 KB]
Test 8: TEST OK [0.000 secs, 3364 KB]
Test 9: TEST OK [0.000 secs, 3364 KB]

All tests OK.
YOUR PROGRAM ('heritage') WORKED FIRST TIME!  That's fantastic
-- and a rare thing.  Please accept these special automated
congratulations.

Here are the test data inputs:

------- test 1 ----
ABEDFCHG CBADEFGH------- test 2 ----
F
F
------- test 3 ----
BCAD
ABCD
------- test 4 ----
GOLEAFS
SFAELOG
------- test 5 ----
GSHBAQTPM
ABGHSPQTM
------- test 6 ----
AUBYCVDZEWFXGTH
ZYUABVCDXWEFTGH
------- test 7 ----
ABDCJHKILMNPOQFEGRS
ABCDEFHJIKLMNOPQGRS
------- test 8 ----
GFDIHKLJMBNESRTPOQAUCWVZYX
ABDFGHIJKLMENOPRSTQCUVWXYZ
------- test 9 ----
EHGDIFJLKMBNCOQSPRAWUXZYTV
ABDEGHFIJKLMCNOPQSRTUWXYZV

Keep up the good work!

Thanks for your submission!

American Heritage

Russ Cox

Here's one way: We use a recursive procedure to generate the actual binary tree. Once we have the tree, we execute a postorder traversal to print the node values.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

FILE *fout;

typedef struct Tree Tree;
struct Tree {
int c;
Tree *left;
Tree *right;
};

Tree*
mktree(int c, Tree *left, Tree *right)
{
Tree *t;
t = (Tree*)malloc(sizeof *t);
assert(t);
t->c = c;
t->left = left;
t->right = right;
return t;
}

/*
* Pre and in point at strings of length len
* that describe the same tree, in pre-order
* and in-order traversals.  Return a
* corresponding binary tree.
*/
Tree*
prein2tree(char *pre, char *in, int len)
{
char *p;
int llen, rlen;

assert(strlen(pre)>=len && strlen(in)>=len);

if(len == 0)
return NULL;

/*
* The first character of the preorder traversal is the root.
* If we find the root in the inorder traversal, then everything
* to its left is on the left side, and everything to its right on the
* right side.  Recur on both sides.
*/
p = strchr(in, pre[0]);
assert(p != NULL);
assert(p-in < len);

llen = p-in;
rlen = len-llen-1;
return mktree(pre[0], prein2tree(pre+1, in, llen), prein2tree(pre+1+llen, p+1, rlen));
}

void
postorder(Tree *t)
{
if(t == NULL)
return;
postorder(t->left);
postorder(t->right);
fprintf(fout, "%c", t->c);
}

void
main(void)
{
FILE *fin;
char pre[50], in[50];

fin = fopen("heritage.in", "r");
fout = fopen("heritage.out", "w");
assert(fin != NULL && fout != NULL);

fscanf(fin, "%s %s", in, pre);
postorder(prein2tree(pre, in, strlen(pre)));
fprintf(fout, "\n");
exit(0);
}
[code] From Greg Price:

We don't need to reconstruct the original tree explicitly for this
problem.  Instead, we use a recursive function that plucks the root from
the start of the preorder traversal, uses it to divide the inorder
traversal, calls itself recursively on the left and right subtrees, and
outputs the root.

#include <fstream.h>
#include <string.h>

ifstream fin("heritage.in");
ofstream fout("heritage.out");

const short maxn = 26 + 2;

short len;
char in[maxn], pre[maxn];

void
makepost(short ina, short inb, short prea, short preb)
{
char root;
short rt_in;
short lsize, rsize;

if (ina == inb) // Null tree
return;

root = pre[prea];

// Find root in inorder
for (rt_in = ina; rt_in < inb; rt_in++)
if (in[rt_in] == root)
break;

// Size of left-hand subtree
lsize = rt_in -  ina;

makepost(ina,     rt_in, prea+1,         prea+1 + lsize); // Left
subtree
makepost(rt_in+1, inb,   prea+1 + lsize, preb); // Right subtree
fout << root;
}

void
main()
{
fin.getline(in , maxn);
fin.getline(pre, maxn);

len = strlen(in);

makepost(0, len, 0, len);
fout << endl;
}

Another Solution -- Daniel Bundala

Here is a better and faster solution for problem "heritage". I usedstandard recursive approach like solutions in analysis for theproblem. But insert new element into the tree can be made faster.Because solutions in analysis in each recursive call are findingthe position of inserting element in in-order representation of thetree. It is O(N) in O(N) calls worth case = O(N^2).

But what we can do is to precompute positions of elements in in-orderrepresentation and when we are inserting new elment e into the noden, e is in left subtree if and only if position_in_inorder[i] <position_in_inorder[n->value] otherwise element e is in rightsubtree.

Now inserting: O(1) in O(N) recursive call - worth case = O(N).All algorithm is O(N^2) and before was O(N^3) - worth case. And O(Nlog N) now, before O(N^2) - average case; N = number of cows

My implementation (I'm using index into the array instead of pointer):

[code]/*
LANG: C++
PROG: heritage
*/

#include <stdio.h>
#include <string.h>

FILE *in,*out;

char ino[200], preo[200];
int place[255];

struct NODE{
int left, right;
char name;
};

NODE node[200];
int count = 1;

//return true if who is before the value of node
in inorder; O(1)
bool isBefore(char who, int n){
return place[who] < place[node
.name];
};

void insert(char c, int where){
if (isBefore(c, where)){ //in the left subtree
if (node[where].left == -1){ //left subtree is empty
node[count].left = node[count].right = -1;
node[count].name = c;
node[where].left = count++;
} else {
insert(c, node[where].left); //insert into the left subtree
};
} else { // in the right subtree
if (node[where].right == -1){ //right subtree is empty
node[count].left = node[count].right = -1;
node[count].name = c;
node[where].right = count++;
} else {
insert(c, node[where].right); //insert into the right subtree
};
};

};

void write(int kde){
if (kde==-1) return;
write(node[kde].left);
write(node[kde].right);
fprintf(out, "%c", node[kde].name);
};

int main ()
{
in=fopen("heritage.in","r");
out = fopen ("heritage.out", "w");
fgets(ino, 200, in);
fgets(preo, 200, in);

for (int i=0; i<strlen(ino); i++)
place[ino[i]] = i;

//insert root
node[0].left = node[0].right = -1;
node[0].name = preo[0];

//insert next element
for (int i=1; i<strlen(ino) - 1; i++)
insert(preo[i], 0);

write(0); fprintf(out, "\n");

fclose(in); fclose(out);
return 0;
}
India's Nishant Redkar had a much simpler solution:
#include <fstream>
#include <string>
using namespace std;

ofstream out ("heritage.out");

void
recursex (string sin, string spre) {
int i;
if (sin.length() < 2) {
out << sin;
return;
}
for (i = 0; sin[i] != spre[0]; i++);
recursex (sin.substr(0,i), spre.substr(1,i));
recursex (sin.substr(i+1), spre.substr(i+1));
out << spre[0];
}

int
main () {
string sin, spre;
ifstream in("heritage.in");
in >> sin >> spre;
recursex (sin, spre);
out << endl;
in.close();
out.close();
return 0;
}
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: