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

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);
}
 
 

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):

/*
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[n] in inorder; O(1)
bool isBefore(char who, int n){
  return place[who] < place[node[n].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;
}


转载于:https://www.cnblogs.com/nealgavin/archive/2013/03/07/3206135.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值