20. 트리의 기초
트리(Tree)란?
트리는 노드와 간선으로 이루어진 비순환 연결 그래프입니다. 그래프의 일종이지만, 사이클이 존재하지 않고 하나의 루트 노드에서 출발해 모든 노드를 연결한다는 점에서 특별한 구조를 가집니다.
트리의 특징
- 순환 구조가 없다 (사이클 없음)
- 정확히 하나의 루트 노드가 존재
- 루트를 제외한 모든 노드는 단 하나의 부모를 가짐
- 트리의 부분 구조(서브트리)도 트리이다
트리 문제를 푸는 방식
트리는 일반 그래프와는 달리 특수한 구조를 이용해 DFS, BFS, 또는 **트리 특화 알고리즘(LCA, 세그먼트 트리 등)**으로 문제를 풉니다.
트리 문제 유형
-
그래프로 푸는 트리
- 인접 리스트/행렬로 그래프처럼 저장하고 DFS/BFS로 탐색
-
트리 전용 문제
- 이진 트리, LCA(최소 공통 조상), 세그먼트 트리 등
예제: 백준 11725번 “트리의 부모 찾기”
문제 설명
- 루트 노드가 1인 트리가 주어진다.
- 각 노드의 부모 노드를 출력하라.
- 입력은 노드 수 N과 N-1개의 간선이 주어지며, 트리를 구성한다.
코드 구현
import sys
sys.setrecursionlimit(10**6)
N = int(sys.stdin.readline().strip())
visited = [False] * (N + 1)
tree = [[] for _ in range(N+1)]
answer = [0] * (N + 1)
# 간선 정보 저장
for _ in range(1, N):
n1, n2 = map(int, sys.stdin.readline().split())
tree[n1].append(n2)
tree[n2].append(n1)
# DFS 탐색으로 부모 찾기
def dfs(number):
visited[number] = True
for i in tree[number]:
if not visited[i]:
answer[i] = number # 부모 노드 기록
dfs(i)
dfs(1) # 루트 노드에서 탐색 시작
# 결과 출력
for i in range(2, N+1):
print(answer[i])
코드 분석
1. 트리 구조 저장
tree = [[] for _ in range(N+1)]
for _ in range(1, N):
n1, n2 = map(int, sys.stdin.readline().split())
tree[n1].append(n2)
tree[n2].append(n1)
트리를 양방향 그래프처럼 인접 리스트로 저장합니다. 트리는 사이클이 없기에 이 구조로 DFS 시 부모를 명확히 구분할 수 있습니다.
2. DFS로 부모 노드 찾기
def dfs(number):
visited[number] = True
for i in tree[number]:
if not visited[i]:
answer[i] = number
dfs(i)
방문한 노드를 체크하며, 방문하지 않은 자식 노드의 부모를 현재 노드로 설정합니다. DFS의 깊이 우선 탐색으로 트리 전체를 탐색합니다.
3. 출력 처리
for i in range(2, N+1):
print(answer[i])
루트 노드를 제외한 노드들의 부모를 출력합니다.
정리
항목 | 설명 |
---|---|
알고리즘 유형 | DFS 기반 트리 탐색 |
주요 조건 | 루트는 1번, 부모는 DFS를 통해 설정 |
핵심 자료구조 | 인접 리스트, 방문 배열, 정답 배열 |
시간 복잡도 | O(N) - 노드 수만큼 탐색 |
응용 예시 | 부모 탐색, 서브트리 계산, 트리 구조 파악 등 |