トップページ -> AOJの解答例 -> ALDS1_7の解答例

ALDS1_7の解答例(Python)

木構造に関する問題です. アルゴリズムについて詳しく知りたい方は参考文献などをご覧ください.

1. ALDS1_7_A: Rooted Trees

与えられた根付き木をnode {node}: parent = {parent}, depth = {depth}, {node_type}, {children}の形式で 出力する問題です. 根から下っていくように探索するようにしました.

# ALDS1_7_A

n = int(input())

info_dict = {} 
children_set = set([i for i in range(n)])

for _ in range(n):
    info = list(map(int,input().split(" ")))
    idx,k,children = info[0],info[1],info[2:]
    info_dict[idx] = [idx,children] # node:idxの情報を持っておく
    children_set = children_set - set(children) # 根を探しておく

tree_dict = {}
children_list = [[list(children_set)[0],-1,0]] # 根からスタート (idx, parent, depth)

# children_list が空になるまでループ
while children_list:
    idx, parent, depth = children_list.pop() #
    if parent == -1:
        tree_dict[idx] = [idx,parent,depth,"root",info_dict[idx][1]]
    elif info_dict[idx][1]:
        tree_dict[idx] = [idx,parent,depth,"internal node",info_dict[idx][1]]
    else:
        tree_dict[idx] = [idx,parent,depth,"leaf",info_dict[idx][1]]
    
    # 子の (idx, parent, depth) をchildren_listに追加する
    for child in info_dict[idx][1]:
        children_list.append([child,idx,depth+1])

for i in range(n):
    # node 0: parent = -1, depth = 0, root, [1, 4, 10]の形式で出力
    print(f"node {i}: parent = {tree_dict[i][1]}, depth = {tree_dict[i][2]}, {tree_dict[i][3]}, {tree_dict[i][4]}")

2. ALDS1_7_B: Binary Trees

二分木に関する問題です. 二分木の性質などは使わずに単純にAのコードを流用しています.

# ALDS1_7_B
n = int(input())
binary_tree = {} 

info_dict = {}
children_set = set([i for i in range(n)])
for _ in range(n):
    idx, left, right = map(int,input().split(" "))
    info_dict[idx] = [left,right]
    children_set = children_set - {left,right} # 根を探しておく

children_list = [[list(children_set)[0],-1,-1,0]] 

leaf_set = set()
# children_listが空になるまでループ
while children_list:
    idx, sibling, parent,depth = children_list.pop()
    # node 0: parent = -1, sibling = -1, degree = 2, depth = 0, height = 3, root
    degree = 0
    for x in info_dict[idx]:
        if x != -1:
            degree += 1
    # nodeのtype別に処理
    if parent == -1:
        binary_tree[idx] = [idx,parent,sibling,degree,depth,0,"root"]
    elif info_dict[idx] == [-1,-1]:
        binary_tree[idx] = [idx,parent,sibling,degree,depth,0,"leaf"]
        leaf_set.add(idx)
    else:
        binary_tree[idx] = [idx,parent,sibling,degree,depth,0,"internal node"]
    
    # left, rightが-1でなければchildren_listに追加
    if info_dict[idx][0] != -1:
        children_list.append([info_dict[idx][0],info_dict[idx][1],idx,depth+1])
    if info_dict[idx][1] != -1:
        children_list.append([info_dict[idx][1],info_dict[idx][0],idx,depth+1])

# 高さを調べる
for idx in leaf_set:
    height = 0
    while True:
        parent = binary_tree[idx][1]
        if parent == -1:
            break
        height += 1
        binary_tree[parent][5] = max(height,binary_tree[parent][5])
            
        idx = parent
            
for i in range(n):
    # node 0: parent = -1, sibling = -1, degree = 2, depth = 0, height = 3, root の形式で出力
    idx,parent,sibling,degree,depth,height,node_type = binary_tree[i]
    print(f"node {idx}: parent = {parent}, sibling = {sibling}, degree = {degree}, depth = {depth}, height = {height}, {node_type}")

3. ALDS1_7_C: Tree Walk

木の巡回に関する問題です. Preorder, Inorder, Postorderの処理をそれぞれ書いています. この問題はそのまま書いてしまっていますが,そのまま書くと余りにも酷いのでNodeクラスを自作して木を表現したものがDにあります. やっていることは変わりませんが,まだDのコードのほうが分かりやすいかもしれません.(?)

# ALDS1_7_C 
n = int(input())
binary_tree = {} 

info_dict = {}
children_set = set([i for i in range(n)])
for _ in range(n):
    idx, left, right = map(int,input().split(" "))
    info_dict[idx] = [left,right]
    children_set = children_set - {left,right} # 根を探しておく
    
root = list(children_set)[0]
children_list = [[list(children_set)[0],-1,-1,0]] 

leaf_set = set()
# children_listが空になるまでループ
while children_list:
    idx, sibling, parent,depth = children_list.pop()
    # node 0: parent = -1, sibling = -1, degree = 2, depth = 0, height = 3, root
    degree = 0
    for x in info_dict[idx]:
        if x != -1:
            degree += 1
    # nodeのtype別に処理
    if parent == -1:
        binary_tree[idx] = [idx,parent,sibling,degree,depth,0,"root",info_dict[idx]]
    elif info_dict[idx] == [-1,-1]:
        binary_tree[idx] = [idx,parent,sibling,degree,depth,0,"leaf",info_dict[idx]]
        leaf_set.add(idx)
    else:
        binary_tree[idx] = [idx,parent,sibling,degree,depth,0,"internal node",info_dict[idx]]
    
    # left, rightが-1でなければchildren_listに追加
    if info_dict[idx][0] != -1:
        children_list.append([info_dict[idx][0],info_dict[idx][1],idx,depth+1])
    if info_dict[idx][1] != -1:
        children_list.append([info_dict[idx][1],info_dict[idx][0],idx,depth+1])
        
def pre_oder(root):
    node_list = [root]
    idx = root
    
    while True:
        # left_forward
        if idx == -1:
            break
        left = binary_tree[idx][-1][0]
        if left != -1 and left not in set(node_list):
            node_list.append(left)
            idx = left

        elif left == -1:
            while True:
                # right_forward
                if idx == -1:
                    break
                right = binary_tree[idx][-1][1]
                if right == -1 or right in set(node_list):
                    idx = binary_tree[idx][1] # 親に戻る
                else:
                    if right == root:
                        break
                    elif right in set(node_list):
                        pass
                    else:
                        node_list.append(right)
                        idx = right
                    
                    break
                    
            if right == root:
                break
            
    print("Preorder")
    print(" ",end="")
    print(*node_list)

def in_oder(root):
    node_list = []
    idx = root
    while True:
        # left_forward
        if idx == -1:
            break
        left = binary_tree[idx][-1][0]
        if left != -1 and left not in set(node_list):
            idx = left
        else:
            node_list.append(idx)
            while True:
                # right_forward
                if idx == -1:
                    break
                right = binary_tree[idx][-1][1]
                if right == -1 or right in set(node_list):
                    idx = binary_tree[idx][1] # 親に戻る
                    if idx not in node_list:
                        if idx != -1:
                            node_list.append(idx)
                else:
                    if right == root:
                        break
                    elif right in set(node_list):
                        pass
                    else:
                        idx = right
                    
                    break
                    
            if right == root:
                break
          
    print("Inorder")
    print(" ",end="")
    print(*node_list)
    
# 節点は右の要素から戻ってきたときに追加する
def post_oder(root):
    node_list = []
    idx = root
    while True:
        # left_forward
        if idx == -1:
            break
        left = binary_tree[idx][-1][0]
        if left != -1 and left not in set(node_list):
            idx = left
        else:
            if binary_tree[idx][-1][1] == -1:
                node_list.append(idx)
            while True:
                # right_forward
                if idx == -1:
                    break
                right = binary_tree[idx][-1][1]
                if right == -1 or right in set(node_list):
                    child = idx
                    idx = binary_tree[idx][1] # 親に戻る
                    if child in node_list and idx not in node_list:
                        # 右側から戻ってきているなら追加
                        if idx != -1:
                            if binary_tree[idx][-1][1] == child:
                                node_list.append(idx)
                            # 左からだが右がないなら追加
                            elif binary_tree[idx][-1][1] == -1:
                                node_list.append(idx)
                                
                    
                else:
                    if right == root:
                        break
                    elif right in set(node_list):
                        pass
                    else:
                        idx = right
                    
                    break
                    
            if right == root:
                break
          
    print("Postorder")
    print(" ",end="")
    print(*node_list)
    
pre_oder(root)
in_oder(root)
post_oder(root)

4. ALDS1_7_D: Reconstruction of a Tree

与えられた先行順巡回 (preorder tree walk) と中間順巡回 (inorder tree walk) を行って得られる節点の列に対して後行順巡回 (postorder tree walk) で得られる節点の列を出力する問題です. 先行順巡回で最初に現れるものが根です.中間順巡回を見つけた根で左右に分けるとその根を親とする左部分木と右部分木が見つかります. この操作を繰り返すことで木を再構築することができます. 前回のコードを使ってと言いたいところですが,そのままでは余りにも使いづらいのでNodeクラスを自作して書き直しました.

# ALDS1_7_D
# 左 根 右 葉まで下る 左側の要素がなければ(根節点なので)追加 それ以外の点は下から戻ってきたときに追加する
def inOrder(root):
    x = tree[root]
    node_list = []
    node_set = set()
    while x!=None:
        flag = True
        # 左側の要素があるかを調べる
        if x.left != None:
            if x.left.key not in node_set:
                x = x.left
                flag = False
        else:
            # 左側の要素がない場合は追加してよい
            if x.key not in node_set:
                node_list.append(x.key)
                node_set.add(x.key)
        # 無ければ右側を調べる
        if flag:
            flag = True
            # 葉までたどり着いたら追加
            if x.right == None:
                x = x.parent
                if x.key not in node_set:
                    node_list.append(x.key)
                    node_set.add(x.key)
                flag = False
            if flag:
                # まだ調べていない場合は下る
                if x.right.key not in node_set:
                    x = x.right
                # 既に調べてある場合は上る
                else:
                    x = x.parent
                    if x != None:
                        if x.key not in node_set:
                            node_list.append(x.key)
                            node_set.add(x.key)
        
    # print(" ",end="") # 問題に合わせて空白を入れただけ
    # print(*node_list)
    
# 下るときに追加していけばいい 葉までたどり着いたら右側の要素が追加されていないところまで戻る
def preOrder(root):
    x = tree[root]
    node_list = [root]
    node_set = set([root])
    while x!=None:
        # # print(node_list)
        flag = True
        # 左側の要素があるかを調べる
        if x.left != None:
            if x.left.key not in node_set:
                x = x.left
                node_list.append(x.key)
                node_set.add(x.key)
                flag = False
                
        # 無ければ右側を調べる
        if flag:
            flag = True
            # 葉までたどり着いたら戻る
            if x.right == None:
                x = x.parent
                flag = False
            if flag:
                # まだ調べていない場合は下る
                if x.right.key not in node_set:
                    x = x.right
                    node_list.append(x.key)
                    node_set.add(x.key)
                # 既に調べてある場合は上る
                else:
                    if x != None:
                        x = x.parent
          
    # print(" ",end="") # 問題に合わせて空白を入れただけ
    # print(*node_list)

def postOrder(root):
    x = tree[root]
    node_list = []
    node_set = set()
    while x!=None:
        # print(x.key)
        flag = True
        # 左側の要素があるかを調べる
        if x.left != None:
            if x.left.key not in node_set:
                x = x.left
                flag = False
                
        # 無ければ右側を調べる
        if flag:
            flag = True
            # 葉までたどり着いたら追加
            if x.right == None:
                node_list.append(x.key)
                node_set.add(x.key)
                x = x.parent
                flag = False
            if flag:
                # まだ調べていない場合は下る
                if x.right.key not in node_set:
                    x = x.right
                # 既に調べてある場合は上る
                else:
                    if x != None:
                        # 右側から戻ってきていれば追加
                        # print(x.key,x.right.key)
                        if x.key not in node_set and x.right.key in node_set:
                            node_list.append(x.key)
                            node_set.add(x.key)
                            x = x.parent
     
    # print(" ",end="") # 問題に合わせて空白を入れただけ
    print(*node_list)
    
# Nodeクラスを自作して木を表現する
class Node:
    def __init__(self,key):
        self.key = key
        self.right = None # 右の子
        self.left = None #左の子
        self.parent = None # 親
        
n = int(input())
preorder_tree = list(map(int,input().split()))
inorder_tree = list(map(int,input().split()))

# preorderで根を見つける
root = preorder_tree.pop(0)
root0 = root # 一番最初の根
# inorder_treeを使って根の左と右で分ける
left_tree = [] # 中間順巡回 の根の左側は左部分木
right_tree = [] # 中間順巡回 の根の右側は右部分木
left = True
for x in inorder_tree:
    if x == root:
        left = False
        continue
    if left:
        left_tree.append(x)
    else:
        right_tree.append(x)
        
# root, parent, left_tree, right_treeのリストを作る
parent = None 
tree_list = [[root,parent,left_tree,right_tree]]
node_list = []
tree = {root0:Node(root0)}
while tree_list:
    root,parent,left_tree,right_tree = tree_list.pop(0)
    node = tree[root]
    
    left_root = None
    right_root = None
    # 部分木の根を見つける(先行順巡回で最初にぶつかったものが根)
    for x in preorder_tree:
        if x in left_tree and left_root==None:
            left_root = x
            # inorder_treeを使って根の左と右で分ける
            left_tree2 = []
            right_tree2 = []
            left = True
            for y in left_tree:
                if y == left_root:
                    left = False
                    continue
                if left:
                    left_tree2.append(y)
                else:
                    right_tree2.append(y)
                    
            tree_list.append([left_root,node,left_tree2,right_tree2])
            
        elif x in right_tree and right_root==None:
            right_root = x
            # inorder_treeを使って根の左と右で分ける
            left_tree2 = []
            right_tree2 = []
            left = True
            for y in right_tree:
                if y == right_root:
                    left = False
                    continue
                if left:
                    left_tree2.append(y)
                else:
                    right_tree2.append(y)
                    
            tree_list.append([right_root,node,left_tree2,right_tree2])
            break
    
    # 調べ終わった要素を先行順巡回のリストから取り除き,Nodeを更新
    if left_root!=None:
        preorder_tree.remove(left_root)
        left_node = Node(left_root)
        node.left = left_node
        tree[left_root] = left_node
    if right_root!=None:
        preorder_tree.remove(right_root)
        right_node = Node(right_root)
        node.right = right_node
        tree[right_root] = right_node
            
    
    # 親を設定する
    node.parent = parent
    
    node_list.append(node)
    tree[node.key] = node
    
# 出来上がった木を表示
postOrder(root0)

<- 前へ戻る 【目次に戻る】 次へ進む ->