Uva 10410 层序+前序构造树

原题

非常经典也比较有意思的一题, 由树的层序遍历和前序遍历构造树.
但是我们都知道, 没有中序的话构造出来的树的可能情况是很多的,
因此题目加了一个很关键的限制条件, 就是遍历序列都是先从点数小的子节点开始访问的.
一开始没注意到这个关键, 空想了很久也没想出来比较好的方法.

其实思路和其他同类的问题(根据两个遍历序列构造树)的思路完全一样, 都是以一个序列为标准, 对另一个序列不断分解成几段, 每一段就是一棵子树中的所有结点.

思路 : 比如像这道题按层序和前序构造树, 可以看到如果在层序序列中同一层中选定相邻两个节点, 那么在先序序列中这两个节点之间的子序列, 就是选定的第一个节点的子树的所有结点. 按这种想法递归进行, 就能划分出一棵棵子树了. 另一个难点在于如何判断层序序列中同一层的结点, 观察之后不难发现, 如果A, B是同一层的结点, 那么在先序序列中B必定排在A之后, 以此类推.

把思路弄清楚就可以开始写代码划分子树了. 还是要注意必须是由小的子节点开始遍历的.

AC代码如下 :
(因为题目中没说是二叉树, 就写了一个可以判断多叉树的, 但写得不是很美观, 所以乍一看可能有点费解..)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <cassert>
#include <iomanip>

//#pragma warning(disable:4996) //关掉4996警告

/*
Uva 10410 Tree Reconstruction

关键 : Note that when a parent was expanded the children were traversed 
            in ascending order. 就是说遍历子节点不是随便遍历的, 必须按由小到大的顺序遍历

*/

using namespace std;

int N;

struct Node {
    int left, right;
    int parent;
    Node (int _a, int _b, int _c) : left (_a), right (_b), parent (_c) { }
};

map<int, vector<int> > Childs;
vector<int> PreOrder;
queue<int> LevelOrder;

void solve ( ) {
    queue<Node> Q;
    LevelOrder.pop ( );
    Q.push (Node(1, N-1, PreOrder[0]));

    while ( !Q.empty ( ) ) {
        int left = Q.front ( ).left, right = Q.front ( ).right, parent = Q.front ( ).parent; Q.pop ( );
        if ( left > right ) continue;

        Childs[parent].push_back (PreOrder[left]);
        LevelOrder.pop ( );
        for ( int i = left+1; i <= right; i++ ) {   // only if left < right
            int next = LevelOrder.front ( ); 
            if ( PreOrder[i] == next && PreOrder[left] < PreOrder[i] ) {        // Because traverse childs in ascending order
                Q.push (Node (left + 1, i - 1, PreOrder[left]));
                Childs[parent].push_back (PreOrder[i]);
                LevelOrder.pop ( );
                left = i;
            }
        }
        if ( left < right ) Q.push (Node (left+1, right, PreOrder[left]));
    }
}


int main( ) {;
    while ( cin >> N ) {
        Childs.clear ( );
        PreOrder.clear ( );
        while ( !LevelOrder.empty() ) {
            LevelOrder.pop ( );
        }
        for ( int i = 0, tmp; i < N; i++ ) {
            cin >> tmp;
            LevelOrder.push (tmp);
        }
        for ( int i = 0, tmp; i < N; i++ ) {
            cin >> tmp;
            PreOrder.push_back (tmp);
        }
        solve ( );
        for ( int j = 1; j <= N; j++ ) {
            cout << j << ":";
            for ( int i = 0; i < Childs[j].size ( ); i++ ) {
                cout << " " << Childs[j][i];
            }
            cout << endl;
        }
    }
    //system ("pause");
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值