본문으로 건너뛰기
김신건의 로그

트리의 지름 (Tree Diameter)

· 수정 · 📖 약 3분 · 1,145자/단어 #algorithm #tree #diameter #dfs #bfs #tree-dp
tree diameter, 트리의 지름, 트리 지름

정의

트리의 지름 (Tree Diameter) 는 트리에서 가장 먼 두 노드 사이의 거리 (경로 길이). 가중치 없는 트리는 간선 개수, 가중치 트리는 간선 가중치 합.

대표 알고리즘:

  1. 두 번 BFS/DFS: 임의 노드에서 가장 먼 노드 u 찾고, u 에서 가장 먼 노드까지 거리가 지름. O(N).
  2. 트리 DP: 각 노드를 루트로 한 서브트리의 최장 경로 두 개를 합쳐 지름 후보. O(N).

문제 상황과 동기

“트리에서 가장 긴 경로” 가 필요한 상황.

  • naive: 모든 노드 쌍 (u, v) 거리 계산. O(N^2). N=10^5 면 10^10.
  • 두 번 DFS: O(N). 핵심: 트리에서 임의 노드 x 에서 가장 먼 노드는 지름의 한 끝점.
  • 트리 DP: O(N). 각 서브트리에서 가장 긴 두 경로를 DP 로 관리.

핵심 통찰: 트리는 사이클이 없으므로, 지름의 두 끝점 중 하나는 임의 시작점에서 가장 먼 노드.

시각화

핵심 아이디어

두 번 BFS/DFS 방법

  1. 임의 노드 x (보통 0) 에서 BFS/DFS → 가장 먼 노드 u.
  2. u 에서 BFS/DFS → 가장 먼 노드 v.
  3. dist(u, v) 가 지름.

증명 스케치: x 에서 가장 먼 노드 u 는 지름 경로의 끝점 중 하나거나, 지름 경로 밖 노드. 후자라면 지름 경로와 x-u 경로가 어딘가에서 만나는데, 그 교점에서 지름 끝점까지 거리가 x-u 보다 멀어야 하므로 모순.

트리 DP 방법

각 노드 u 에 대해:

  • h[u] = u 를 루트로 한 서브트리에서 u 에서 시작하는 최장 경로.
  • 자식 v 들의 h[v] 중 가장 긴 두 개를 합치면 u 를 지나는 최장 경로 → 지름 후보.
h[u] = max(h[v] + w(u, v))  for all child v
diameter = max(h[child1] + h[child2] + w(u, child1) + w(u, child2))

알고리즘

두 번 BFS

bfs(start):
    queue = [start], dist = {start: 0}
    while queue not empty:
        u = queue.pop()
        for v in adj[u]:
            if v not in dist:
                dist[v] = dist[u] + 1
                queue.push(v)
    return argmax(dist), max(dist)

find_diameter():
    u, _ = bfs(0)
    v, d = bfs(u)
    return d

트리 DP

dfs(u, parent):
    h[u] = 0
    top2 = [0, 0]            # 자식 중 가장 긴 두 경로
    for v in adj[u]:
        if v == parent: continue
        dfs(v, u)
        top2.append(h[v] + 1)
        top2.sort(reverse=True)
        top2 = top2[:2]
    h[u] = top2[0]
    diameter = max(diameter, top2[0] + top2[1])

find_diameter_dp():
    dfs(0, -1)
    return diameter

구현

// 두 번 BFS, O(N)
#include <bits/stdc++.h>
using namespace std;
vector<int> adj[100005];
int N;

pair<int, int> bfs(int start) {
  vector<int> dist(N, -1);
  queue<int> q; q.push(start); dist[start] = 0;
  int farthest = start, maxDist = 0;
  while (!q.empty()) {
      int u = q.front(); q.pop();
      for (int v : adj[u]) {
          if (dist[v] == -1) {
              dist[v] = dist[u] + 1;
              q.push(v);
              if (dist[v] > maxDist) {
                  maxDist = dist[v];
                  farthest = v;
              }
          }
      }
  }
  return {farthest, maxDist};
}

int main() {
  cin >> N;
  for (int i = 0; i < N - 1; i++) {
      int a, b; cin >> a >> b; a--; b--;
      adj[a].push_back(b); adj[b].push_back(a);
  }
  auto [u, _] = bfs(0);
  auto [v, d] = bfs(u);
  cout << d << "\n";
}
stdin
5
1 2
2 3
3 4
4 5
결과
4

복잡도

항목
두 번 BFS/DFSO(N) 시간, O(N) 공간
트리 DPO(N) 시간, O(N) 공간 (재귀 스택)

두 방법 모두 선형 시간, 가중치 트리도 간선 가중치만 바꾸면 동일.

변형 / 활용

응용설명
트리의 중심 (center)지름의 중점. 지름이 짝수면 간선 위, 홀수면 노드.
트리의 반지름 (radius)중심에서 가장 먼 노드까지 거리 = ⌈diameter / 2⌉.
가중치 트리간선 가중치 있을 때도 두 번 BFS + DP 모두 O(N).
모든 노드 쌍 최단거리지름만 아니라 전체 거리 행렬 필요하면 O(N^2), 또는 [[LCA
동적 트리 지름간선 추가/삭제 시 지름 유지 → [[Link-Cut Tree

함정

1. 루트 선택 무관하다는 착각

두 번 BFS 는 임의 노드에서 시작해도 되지만, 트리 DP 는 루트 선택이 구현 편의상 0 으로 고정해도 무방. 단, 지름 자체는 루트 무관.

2. 가중치 음수

일반 트리에서 음수 가중치 없지만, 문제에 따라 있을 수도. 두 번 BFS 는 가중치 음수 불가 (Bellman-Ford 필요). 트리 DP 는 음수 가중치도 처리 가능.

3. 1-indexed 입력

노드 번호 1-indexed 면 a--; b--; 필수.

4. 지름 경로 복원

지름의 길이만 아니라 경로 자체 필요하면, BFS 에서 parent 배열 기록 후 역추적.

BOJ 연습 문제

번호제목정답률링크
BOJ 1167트리의 지름-kokoa-lab
BOJ 1967트리의 지름-kokoa-lab
BOJ 2263트리의 순회-kokoa-lab
BOJ 4803트리-kokoa-lab

참고

이 글의 용어 (4개)
오일러 투어 테크닉 (Euler Tour Technique)algorithm
정의 오일러 투어 테크닉 (ETT, Euler Tour Technique) 은 트리를 DFS 방문 순서로 펼쳐 서브트리 쿼리를 구간 쿼리로 변환하는 정형. 각 노드 u 의 in-…
최소 공통 조상 (Lowest Common Ancestor)algorithm
정의 최소 공통 조상 (LCA, Lowest Common Ancestor) 는 트리에서 두 노드 u, v 의 공통 조상 중 가장 깊은 (루트에서 가장 먼) 노드. Binary L…
Dynamic Tree (Link/Cut Tree, Euler Tour Tree, Top Tree)algorithm
정의 Dynamic Tree 는 트리에 간선 추가 (link) / 제거 (cut) 가 섞이는 환경에서 경로 / 서브트리 집계 쿼리 를 O(log N) 에 처리하는 자료구조 가족.…
Graph DP: 트리/DAG 위 DPalgorithm
정의 Graph DP 는 그래프 구조를 이용한 DP. 사이클 없는 그래프 (트리, DAG) 에서 특히 자연스러움. Tree DP 루트 트리에서 자식 서브트리 결과를 조합. Rer…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기