그래프 이론(Graph Theory)은 꼭짓점(Vertex)과 간선(Edge)으로 이루어진 구조를 연구하는 수학의 한 분야다. 소셜 네트워크, 지도 경로, 컴파일러 최적화, 신경망 아키텍처의 이론적 기반이며, 현실의 네트워크·관계·연결 구조를 수학적으로 표현한다.
기본 정의
그래프 G = (V, E)
V : 꼭짓점(Vertex / Node) 집합
E : 간선(Edge) 집합, e = (u, v)
예)
V = {A, B, C, D}
E = {(A,B), (B,C), (C,D), (A,D)}
주요 용어
| 용어 | 설명 |
|---|
| 인접 (Adjacent) | 간선으로 직접 연결된 두 정점의 관계 |
| 차수 (Degree) | 정점에 연결된 간선의 수. 방향 그래프에서는 진입 차수(in-degree)·진출 차수(out-degree)로 분리 |
| 경로 (Path) | 정점을 순서대로 연결한 길. 같은 정점을 두 번 방문하지 않으면 단순 경로 |
| 사이클 (Cycle) | 출발 정점으로 돌아오는 경로 |
| 연결 그래프 | 임의의 두 정점 사이에 경로가 존재하는 그래프 |
| 트리 (Tree) | 사이클 없는 연결 그래프. 정점 n개 → 간선 n-1개 |
| 가중치 (Weight) | 간선에 부여된 비용·거리·용량 값 |
| 부분 그래프 | 원래 그래프의 일부 정점·간선으로 구성된 그래프 |
| 신장 트리 (Spanning Tree) | 그래프의 모든 정점을 포함하는 트리 |
핸드셰이킹 보조정리: 모든 정점의 차수 합은 간선 수의 2배
Σ deg(v) = 2|E|
그래프의 종류
방향성에 따른 분류
무방향 그래프 (Undirected): A ── B 간선에 방향 없음
방향 그래프 (Directed/Digraph): A ──→ B 간선에 방향 있음
방향 그래프에서:
진입 차수 (in-degree): 해당 정점으로 들어오는 간선 수
진출 차수 (out-degree): 해당 정점에서 나가는 간선 수
순환성에 따른 분류
| 유형 | 설명 | 활용 |
|---|
| 순환 그래프 | 사이클이 존재하는 그래프 | 일반 네트워크 |
| 비순환 그래프 (DAG) | 방향 있고 사이클 없음 | 위상 정렬, 의존성 관리, 빌드 시스템 |
| 트리 | 사이클 없는 연결 그래프 | 계층 구조, 파일 시스템 |
가중치에 따른 분류
비가중치 그래프: 연결 여부만 표현 (간선값 = 1로 가정)
가중치 그래프: 간선마다 비용·거리·용량 값 부여
예) 도시 간 거리 그래프:
서울 ──50km──→ 수원 ──30km──→ 오산
특수 그래프
| 유형 | 설명 |
|---|
| 완전 그래프 (Kₙ) | 모든 정점 쌍이 간선으로 연결. 간선 수 = n(n-1)/2 |
| 이분 그래프 (Bipartite) | 정점을 두 집합으로 나눠 같은 집합 내 간선이 없음 |
| 평면 그래프 (Planar) | 간선이 교차하지 않도록 평면에 그릴 수 있는 그래프 |
| 연결 성분 (Connected Component) | 서로 연결된 정점들의 최대 부분 그래프 |
그래프 표현 방법
인접 행렬 (Adjacency Matrix)
정점 간 연결 여부를 2차원 배열로 표현한다.
python
# 정점 수 n, 간선: (0,1), (0,2), (1,2)
n = 3
matrix = [[0] * n for _ in range(n)]
edges = [(0,1), (0,2), (1,2)]
for u, v in edges:
matrix[u][v] = 1
matrix[v][u] = 1 # 무방향 그래프
# matrix:
# [[0, 1, 1],
# [1, 0, 1],
# [1, 1, 0]]
| 항목 | 인접 행렬 |
|---|
| 공간 복잡도 | O(V²) |
| 간선 존재 확인 | O(1) |
| 이웃 정점 순회 | O(V) |
| 적합한 경우 | 정점 수 적고 간선 밀도 높은 밀집 그래프 |
인접 리스트 (Adjacency List)
각 정점에 연결된 이웃 정점들을 리스트로 저장한다.
python
from collections import defaultdict
graph = defaultdict(list)
edges = [(0,1), (0,2), (1,2), (2,3)]
for u, v in edges:
graph[u].append(v)
graph[v].append(u) # 무방향 그래프
# graph: {0: [1, 2], 1: [0, 2], 2: [0, 1, 3], 3: [2]}
# 가중치 그래프 (u, v, weight)
wgraph = defaultdict(list)
for u, v, w in [(0,1,4), (0,2,2), (1,2,1)]:
wgraph[u].append((v, w))
wgraph[v].append((u, w))
| 항목 | 인접 리스트 |
|---|
| 공간 복잡도 | O(V + E) |
| 간선 존재 확인 | O(degree(v)) |
| 이웃 정점 순회 | O(degree(v)) |
| 적합한 경우 | 간선 수 적은 희소 그래프 (대부분의 실전 문제) |
탐색 알고리즘
BFS (너비 우선 탐색)
큐를 이용해 가까운 정점부터 탐색한다. 비가중치 최단 경로 탐색에 사용된다.
python
from collections import deque
def bfs(graph: dict, start: int) -> list:
visited = {start}
queue = deque([start])
order = []
while queue:
node = queue.popleft()
order.append(node)
for neighbor in graph.get(node, []):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return order
# 시간: O(V+E), 공간: O(V)
DFS (깊이 우선 탐색)
재귀(또는 스택)로 가능한 깊이까지 탐색 후 백트래킹한다. 사이클 탐지, 연결 성분 탐색에 사용된다.
python
def dfs(graph: dict, start: int, visited=None) -> list:
if visited is None:
visited = set()
visited.add(start)
result = [start]
for neighbor in graph.get(start, []):
if neighbor not in visited:
result.extend(dfs(graph, neighbor, visited))
return result
# 시간: O(V+E), 공간: O(V) (재귀 스택)
다익스트라 알고리즘 (Dijkstra)
음수 간선이 없는 가중치 그래프의 단일 출발점 최단 경로를 구한다.
python
import heapq
from collections import defaultdict
def dijkstra(graph: dict, start) -> dict:
dist = defaultdict(lambda: float('inf'))
dist[start] = 0
heap = [(0, start)]
while heap:
d, node = heapq.heappop(heap)
if d > dist[node]:
continue
for neighbor, weight in graph.get(node, []):
nd = d + weight
if nd < dist[neighbor]:
dist[neighbor] = nd
heapq.heappush(heap, (nd, neighbor))
return dict(dist)
# 시간: O((V+E) log V), 공간: O(V)
플로이드-워셜 알고리즘 (Floyd-Warshall)
모든 쌍 최단 경로를 DP로 계산한다. 음수 간선 허용 (단, 음수 사이클 불가).
python
def floyd_warshall(n: int, edges: list) -> list:
INF = float('inf')
dist = [[INF] * n for _ in range(n)]
for i in range(n):
dist[i][i] = 0
for u, v, w in edges:
dist[u][v] = w
for k in range(n): # 경유 정점
for i in range(n): # 출발
for j in range(n): # 도착
if dist[i][k] + dist[k][j] < dist[i][j]:
dist[i][j] = dist[i][k] + dist[k][j]
return dist
# 시간: O(V³), 공간: O(V²)
위상 정렬 (Topological Sort)
DAG에서 의존성 순서대로 정점을 나열한다. 빌드 시스템, 강의 순서, 패키지 의존성에 활용된다.
python
def topological_sort(graph: dict) -> list:
"""Kahn 알고리즘 (진입 차수 기반)"""
in_degree = defaultdict(int)
for node in graph:
for neighbor in graph[node]:
in_degree[neighbor] += 1
queue = deque([n for n in graph if in_degree[n] == 0])
result = []
while queue:
node = queue.popleft()
result.append(node)
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
# 결과 길이 != 정점 수이면 사이클 존재
return result if len(result) == len(graph) else []
최소 신장 트리 (MST)
모든 정점을 최소 비용으로 연결하는 트리를 구한다.
python
# 크루스칼 알고리즘 (Union-Find)
def kruskal(n: int, edges: list) -> int:
parent = list(range(n))
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(a, b):
a, b = find(a), find(b)
if a == b:
return False
parent[b] = a
return True
edges.sort(key=lambda e: e[2]) # 가중치 오름차순
total = 0
for u, v, w in edges:
if union(u, v):
total += w
return total
# 시간: O(E log E)
알고리즘 복잡도 비교
| 알고리즘 | 시간 복잡도 | 공간 복잡도 | 용도 |
|---|
| BFS | O(V+E) | O(V) | 비가중치 최단 경로, 레벨 탐색 |
| DFS | O(V+E) | O(V) | 사이클 탐지, 연결 성분, 위상 정렬 |
| 다익스트라 | O((V+E) log V) | O(V) | 단일 출발점 최단 경로 (음수 간선 불가) |
| 벨만-포드 | O(VE) | O(V) | 단일 출발점 최단 경로 (음수 간선 허용) |
| 플로이드-워셜 | O(V³) | O(V²) | 모든 쌍 최단 경로 |
| 크루스칼 | O(E log E) | O(V) | 최소 신장 트리 |
| 프림 | O((V+E) log V) | O(V) | 최소 신장 트리 (밀집 그래프에 유리) |
| 위상 정렬 | O(V+E) | O(V) | DAG 의존성 순서 결정 |
그래프 표현 방식 선택 기준
정점 수 V, 간선 수 E
밀집 그래프 (E ≈ V²): 인접 행렬 권장
희소 그래프 (E << V²): 인접 리스트 권장
실전 코딩 테스트: 대부분 인접 리스트 사용
→ graph = defaultdict(list)
실제 활용 분야
| 분야 | 활용 사례 |
|---|
| 지도·내비게이션 | 도로망 최단 경로 (다익스트라) |
| 소셜 네트워크 | 친구 추천, 6단계 분리 이론 |
| 컴퓨터 네트워크 | 패킷 라우팅, 네트워크 플로우 |
| 웹 크롤링 | 페이지 간 링크 구조 탐색 (BFS) |
| 컴파일러 | 빌드 의존성 순서 (위상 정렬) |
| 게임 개발 | 맵 경로 탐색 (A*, BFS) |
| 추천 시스템 | 협업 필터링, 이분 그래프 매칭 |
| 생물정보학 | 단백질 상호작용 네트워크 |
장단점
장점
- •현실의 복잡한 관계·구조 모델링에 적합
- •BFS·DFS·다익스트라 등 강력한 알고리즘 존재
- •네트워크·경로·순서 등 다양한 문제 통합 표현 가능
단점
- •구현 복잡도 높음 (인접 리스트·행렬·Union-Find 등 적절한 자료구조 선택 필요)
- •인접 행렬은 O(V²) 공간 낭비 (희소 그래프에 비효율)
- •그래프 크기 증가 시 BFS·DFS O(V+E) 연산 비용 증가
관련 개념
- •알고리즘 — 그래프 알고리즘의 전체 범주
- •자료구조 — 그래프 표현에 사용되는 배열·리스트·힙
- •네트워크 알고리즘 — 그래프 기반 네트워크 플로우·매칭
- •동적 프로그래밍 — 플로이드-워셜 등 DP 기반 그래프 알고리즘
- •트리 — 그래프의 특수 형태 (사이클 없는 연결 그래프)