한양과학기술고등학교

에듀테크 프로젝트 대시보드

그래프 표현 (인접 리스트 vs 행렬)

그래프 표현 — 인접 리스트 vs 인접 행렬

이 사이트엔 이미 BFS/DFS, 다익스트라, 최소 신장 트리 같은 그래프 알고리즘 페이지가 여럿 있지만, 정작 "그래프를 코드로 어떻게 저장하는가"는 다루지 않았다. 알고리즘이 빠르게 도는지는 결국 자료구조 선택에 달려 있다 — 같은 그래프도 인접 행렬과 인접 리스트 중 무엇으로 담느냐에 따라 메모리와 속도가 완전히 달라진다. 이 페이지는 BFS/DFS(/graph-traversal) 같은 탐색 알고리즘의 선행 개념이다.

그래프 기본 용어

그래프(Graph)는 정점(Vertex, 노드) 들과 그것을 잇는 간선(Edge) 들의 모임이다. 간선에 방향이 있으면 방향 그래프(directed), 없으면 무방향 그래프(undirected)라 하고, 간선마다 비용(거리·시간 등) 숫자가 붙으면 가중치 그래프(weighted)라 한다.

무방향 · 무가중치 그래프

A B C D E

간선에 화살표가 없다 — A-B 관계는 곧 B-A 관계와 같다. 친구 관계, 도로망 등.

방향 · 가중치 그래프

3 5 7 P Q R

화살표 방향으로만 이동 가능하고 숫자는 가중치(비용)다. 길찾기·환승 시간·네트워크 대역폭 등.

인접 행렬 (Adjacency Matrix)

정점이 N개면 N×N 표를 만들고, matrix[i][j] = 1이면 i에서 j로 가는 간선이 있다는 뜻이다. "i와 j가 연결돼 있는가?"를 표 한 칸 조회로 바로 알 수 있어 간선 존재 확인이 O(1)이다. 대신 정점 수와 무관하게 항상 N² 칸을 다 차지하므로 공간이 O(V²) — 간선이 거의 없는 그래프에도 빈 칸이 낭비된다.

인접 리스트 (Adjacency List)

각 정점마다 자신과 연결된 정점들의 리스트만 들고 있는다. 실제로 존재하는 간선 수만큼만 저장하므로 공간이 O(V+E) — 정점 목록 V개 + 간선 기록 E개뿐이다. 다만 "i와 j가 연결됐나?"를 확인하려면 i의 리스트를 처음부터 훑어야 하므로 간선 존재 확인이 O(degree(i))로, 행렬보다 느릴 수 있다.

직접 편집해보기 — 행렬 · 리스트 · 그림, 동시에 바뀐다

행렬의 칸을 클릭하면 간선이 켜지거나 꺼진다. 무방향 모드에서는 (i,j)와 (j,i)가 항상 같이 바뀌고, 방향 모드에서는 따로 켤 수 있다 — 오른쪽 그림에서 화살표로 확인해보자.

그래프 그림
인접 행렬
인접 리스트
정점 수 V = 6 · 현재 간선 수 E = 0

인접 행렬

  • 간선 존재 확인: O(1)
  • 공간: O(V²) — 정점 수만 의존
  • 간선이 적어도(희소) 칸은 그대로 다 차지
  • 모든 정점 쌍을 훑어야 하는 계산에 유리

인접 리스트

  • 간선 존재 확인: O(degree(i))
  • 공간: O(V+E) — 실제 간선 수만큼만
  • 희소 그래프에서 메모리를 크게 아낌
  • "이 정점의 이웃들"을 순회하는 계산에 유리 (BFS/DFS 등)

밀집 vs 희소 그래프 — 메모리 트레이드오프

간선 수 E는 위 에디터에서 만든 그래프를 그대로 사용한다. 정점 수 V만 슬라이더로 늘려보면서 두 저장 방식의 메모리 사용량(칸 수)이 어떻게 벌어지는지 비교해보자.

정점 수 V 6 (현재 간선 수 E = 0)
36인접 행렬 (V²)
6인접 리스트 (V+E)
정점 수가 늘어날수록 인접 행렬은 제곱으로, 인접 리스트는 거의 제자리로 커진다. 반대로 위 에디터에서 간선을 계속 추가해 밀집 그래프(거의 모든 쌍이 연결)로 만들면 리스트 크기도 행렬에 가까워진다 — 그 지점부터는 O(1) 조회가 되는 행렬이 실질적으로 더 유리해진다.

언제 뭘 쓰나

상황추천이유
희소 그래프 + BFS/DFS, 다익스트라 인접 리스트 "이 정점의 이웃만" 순회하면 되므로 O(V+E) 공간으로 충분
모든 정점 쌍 최단거리 — 플로이드-워셜 인접 행렬 어차피 모든 (i,j) 쌍을 반복 갱신하므로 O(1) 조회가 유리
밀집 그래프 (E가 V²에 가까움) 인접 행렬 공간 낭비가 거의 없고 조회가 더 빠름
정점 수가 매우 크고 간선은 적음 (SNS, 웹 그래프 등) 인접 리스트 V²는 감당 불가능한 크기가 되기 쉬움

파이썬 코드로 보기

# 인접 리스트: 딕셔너리로 표현
graph_list = {
    'A': ['B', 'D'],
    'B': ['A', 'C'],
    'C': ['B'],
    'D': ['A'],
}

# "B와 C가 연결됐나?" → B의 리스트를 훑어야 함: O(degree)
if 'C' in graph_list['B']:
    print('연결됨')
# 인접 행렬: 2차원 리스트로 표현 (0=A, 1=B, 2=C, 3=D)
graph_matrix = [
    [0, 1, 0, 1],  # A: B, D 와 연결
    [1, 0, 1, 0],  # B: A, C 와 연결
    [0, 1, 0, 0],  # C: B 와 연결
    [1, 0, 0, 0],  # D: A 와 연결
]

# "B(1)와 C(2)가 연결됐나?" → 표 한 칸만 보면 됨: O(1)
if graph_matrix[1][2] == 1:
    print('연결됨')

한 줄 정리

그래프는 정점 + 간선일 뿐이고, 그걸 코드로 담는 방법은 두 가지 — 인접 행렬은 조회가 빠른 대신 정점 수의 제곱만큼 자리를 차지하고, 인접 리스트는 실제 간선만큼만 쓰는 대신 조회가 느리다. 희소 그래프 + BFS/DFS류 → 리스트, 밀집 그래프 + 모든 쌍 계산(플로이드-워셜) → 행렬이 기본 선택이다.