AIZU Online judgeの根付き木の解法メモです。
サンプルのコードがわかりにくかったので、読み解いてみました。
問題は以下になります。
与えられた根付き木 T の各節点 u
について、以下の情報を出力するプログラムを作成してください。
- uの節点番号
- uの親の節点番号
- uの深さ
- uの節点の種類(根、内部節点または葉)
- uの子のリスト
ここでは、与えられる木は n 個の節点を持ち、それぞれ 0 から n-1 の番号が割り当てられているものとします。
入力
入力の最初の行に、節点の個数 n が与えられます。続く n行目に、各節点の情報が次の形式で1行に与えられます。
id k c1 c2 ... ck
idは節点の番号、k は次数を表します。
c1 c2 ...ck は 1 番目の子の節点番号、... k 番目の子の節点番号を示します。
出力方式
次の形式で節点の情報を出力してください。節点の情報はその番号が小さい順に出力してください。
node id: parent = p , depth = d, type, [c1...ck]
pは親の番号を示します。ただし、親を持たない場合は -1 とします。
dは節点の深さを示します。
typeは根、内部節点、葉をそれぞれあらわす root、internal node、leaf の文字列のいずれかです。
ただし、根が葉や内部節点の条件に該当する場合は root とします。
c1...ck は子のリストです。
順序木とみなし入力された順に出力してください。
カンマ空白区切りに注意してください。
出力例にて出力形式を確認してください。
入力例
13 0 3 1 4 10 1 2 2 3 2 0 3 0 4 3 5 6 7 5 0 6 0 7 2 8 9 8 0 9 0 10 2 11 12 11 0 12 0
出力
node 0: parent = -1, depth = 0, root, [1, 4, 10] node 1: parent = 0, depth = 1, internal node, [2, 3] node 2: parent = 1, depth = 2, leaf, [] node 3: parent = 1, depth = 2, leaf, [] node 4: parent = 0, depth = 1, internal node, [5, 6, 7] node 5: parent = 4, depth = 2, leaf, [] node 6: parent = 4, depth = 2, leaf, [] node 7: parent = 4, depth = 2, internal node, [8, 9] node 8: parent = 7, depth = 3, leaf, [] node 9: parent = 7, depth = 3, leaf, [] node 10: parent = 0, depth = 1, internal node, [11, 12] node 11: parent = 10, depth = 2, leaf, [] node 12: parent = 10, depth = 2, leaf, []
問題は以上です。
根付き木とは
根(root)を持つ木構造のことを根付き木といいます。
根は親を持たない唯一の節点のことをいいます。
では、問題を解いていきます。
まず、木の接点となるnodeの定義をします。
// node最大量
#define MAX 100005
#define NIL -1
struct Node {
int parent;
int left;
int right;
};
// 配列で保持する
Node gNode[MAX];
int gDepth[MAX];
構造体としてNodeを定義して
子がないことなどを示すためNILを定義しました。
NILはobjective-cなどにも定義されています。
leftは子という意味合いでrightは同じ階層の兄弟のような意味合いです。
続いて出力関数を定義します。
void print(int node){
cout << "node " << node << ": ";
cout << "parent = " << gNode[node].parent << ", ";
cout << "depth = " << gDepth[node] << ", ";
if(gNode[node].parent == NIL) cout << "root, ";
else if(gNode[node].left == NIL)cout << "leaf, ";
else cout << "internal node, ";
cout << "[";
for(int i = 0,left = gNode[node].left;left != NIL;++i,left = gNode[left].right){
if(i) cout << ", ";
cout << left;
}
cout << "]" << endl;
}
わかりにくいところは、left(左の木)を出力する箇所だと思います。
順番としてまず、対象のnodeのleftを出力します。
続いて、出力したleft nodeに飛び、そのleft(nodeと表現できる)のrightをのleftを出力
これを繰り返します。
続いて深さを求めて、global変数gDepthに代入する関数です。
// 再帰的に深さを求める
void rec(int node ,int parent){
gDepth[node] = parent;
if(gNode[node].right != NIL){
// 右の兄弟に同じ深さを設定
rec(gNode[node].right,parent);
}
if(gNode[node].left != NIL){
// 最も左の子に自分の深さ+1を設定
rec(gNode[node].left,parent + 1);
}
}
道具が揃ったので、main関数を書きます。
int main(){
int nodeNum,nodeIndex,node,left,root,n;
cin >> n;
// 1.初期化
for(int i = 0;i < n;++i){
gNode[i].parent = gNode[i].left = gNode[i].right = NIL;
}
// 2.nodeに代入していく
for(int i = 0;i < n;++i){
cin >> nodeIndex >> nodeNum;
for(int j = 0;j < nodeNum;++j){
cin >> node;
// 初期値はleft
if(j == 0){
gNode[nodeIndex].left = node;
}
else{
gNode[left].right = node;
}
left = node;
gNode[node].parent = nodeIndex;
}
}
// 3.rootを探す
for(int i = 0;i < n;++i){
if(gNode[i].parent == NIL){
root = i;
}
}
// 根からはじめて深さを代入する
rec(root,0);
// 4.nodeのindexごとに出力する
for(int i = 0;i < n;++i){
print(i);
}
return 0;
}
1.初期化
まずは、NILで初期化します。
2.gNodeに入力された情報を埋め込みます。
初期値は必ずleftに2つめ以降はrightに入れていきます。
nodeの親は変わりません。
3.で根をさがしてそのindexを代入しています。
4.最後にprint関数を使い、nodeのindex順に出力します。
以上です。
こちらを参考にしています。
0 件のコメント:
コメントを投稿