木の直径

木の直径、重み付き木の直径のライブラリ作ってないと思って作ってみた。
python です。
操作は、幅優先探索を2回するだけ。
AtCoder Grand Contest で直径を使う問題が出たけど、
直径まで考えが及ばなかったので、覚えるために記事を書いてる。

アルゴリズム

適当な頂点から最も遠い頂点  s をとる。
頂点  s から最も遠い頂点  g までの距離がこの木の「直径」になっている。

木の直径を求めるプログラム

def diameter(N, E):#diamete of a tree
    from collections import deque
    V = [-1]*N
    V[0] = 0
    q = deque([0])
    while q:
        v = q.popleft()
        for w in E[v]:
            if V[w] == -1:
                V[w] = V[v] + 1
                q.append(w)
    s = V.index( max(V))
    p = deque([s])
    W = [-1]*N
    W[s] = 0
    while p:
        v = p.popleft()
        for w in E[v]:
            if W[w] == -1:
                W[w] = W[v] + 1
                p.append(w)
    return max(W)

N = int( input())
E = [ [] for _ in range(N)]
for _ in range(N-1):
    a, b = map( int, input().split())
    a, b = a-1, b-1
    E[a].append(b)
    E[b].append(a)
print( diameter(N, E))

同じように重み付き木の直径を次のように求めることができる。

重み付き木の直径を求めるプログラム
def diameter(N, E, D):#diamete of a tree
from collections import deque
V = [-1]*N
V[0] = 0
q = deque([0])
while q:
v = q.popleft()
for w in E[v]:
if V[w] == -1:
V[w] = V[v] + D[(v,w)]
q.append(w)
s = V.index( max(V))
p = deque([s])
W = [-1]*N
W[s] = 0
while p:
v = p.popleft()
for w in E[v]:
if W[w] == -1:
W[w] = W[v] + D[(v,w)]
p.append(w)
return max(W)

from collections import defaultdict
N = int( input())
E = [ [] for _ in range(N)]
D = defaultdict( int)
for _ in range(N-1):
a, b, w = map( int, input().split())
E[a].append(b)
E[b].append(a)
D[(a,b)] = D[(b,a)] = w
print( diameter( N, E, D))


おまけ

list でも、deque と同様の操作ができるから、list でやることが多かったけど、
どうやら list は O(N) かかるけど、deque は  O(1) でできるらしい。
公式ドキュメントにあった。
collections --- コンテナデータ型 — Python 3.7.3 ドキュメント