【AIZU ONLINE JUDGE】 木の復元の問題を解いてみた
木の復元の問題です。木の巡回の応用問題となってます。
木の復元 | アルゴリズムとデータ構造 | Aizu Online Judge
例
まず、例を見て解き方を考察します。
先行順巡回で P = {1, 2, 3, 4, 5}、中間順巡回で I = {3, 2, 4, 1, 5} という順でノードが訪問される場合を考えます。
(1)
Pより、根は1です。さらに、Iをみると、1の左側に{3, 2, 4}、右側に{5}があることがわかります。 Iは中間順巡回を表しているので、左部分木と右部分木に属するノードが特定できます。
(2)
(1)の右部分木はノードが5だけなので、部分木の構造は自明です。Pの2番目の要素が2であることから、左部分木は2を根とする部分木であることがわかります。さらに、Iから、2の左側に{3}、右側に{4}があることがわかります。
(3)
属しているノードの数が1つだけであることから、ノード3、4は確定します。
解答
再帰的に部分木をたどっていく過程で、部分木に属するノードに対応するIの範囲を狭めていきますが、Iの範囲を表現するためにインデックスright, leftを導入します。rightは該当範囲の先頭、leftは末尾+1です。
解答例はこちら。
#include<iostream> #include<vector> using namespace std; int Pre[100]; // 先行順巡回で得られるノード int In[100]; // 中間順巡回で得られるノード vector<int> Post; // 後行順にノードを格納 int pos; // 部分木のrootのPreでの位置 // 木を復元する void reconstruction(int left, int right) { if (left >= right) return; // 終了条件 int root, root_pos; root = Pre[pos++]; // Preの順番にノードに訪問 // rootのInでの位置を探す root_pos = left; for (int i = left; i < right; i++) { if (In[i] == root) { root_pos = i; } } reconstruction(left, root_pos); // 左部分木 reconstruction(root_pos + 1, right); // 右部分木 Post.push_back(root); // 後行順にノードを格納 } int main() { int i, n, pos; cin >> n; for (i = 0; i < n; i++) cin >> Pre[i]; for (i = 0; i < n; i++) cin >> In[i]; // 木の復元 pos = 0; reconstruction(0, n); // 出力 for (i = 0; i < n; i++) { if (i > 0) cout << " "; cout << Post[i]; } cout << endl; return 0; }
まとめ
木の復元について問題を解きました。やっぱり再帰関数は難しい。
参考
渡部有隆. プログラミングコンテスト攻略のためのアルゴリズムとデータ構造. Ozy, 秋葉拓哉協力. 株式会社マイナビ. 2015.