그래프 — 인접행렬 vs 인접리스트
이 토픽을 마치면
그래프가 무엇인지, 그리고 인접행렬과 인접리스트 두 가지 표현 방식의 차이를 이해하게 됩니다.
트리는 그래프의 특수한 경우입니다
배열, 연결 리스트, 트리 — 지금까지 본 자료구조들은 데이터 사이의 관계가 단순합니다. 배열은 순서, 트리는 계층입니다.
하지만 현실의 관계는 복잡합니다. SNS에서 사람들은 서로 팔로우합니다. 도시들은 도로로 연결됩니다. 웹페이지들은 링크로 이어집니다. 이런 다대다 관계를 표현하는 자료구조가 **그래프(Graph)**입니다.
그래프는 **노드(Node, 또는 정점 Vertex)**와 노드를 잇는 **간선(Edge)**으로 구성됩니다. 트리도 그래프의 일종이지만, "부모-자식" 제약과 "사이클 없음" 조건이 있는 특수한 그래프입니다.
방향 그래프 vs 무방향 그래프
무방향 그래프: A와 B가 연결되면 A→B, B→A 양쪽 다 갈 수 있습니다. 페이스북 친구 관계처럼 — 내가 친구면 상대도 친구입니다.
방향 그래프(Directed Graph): A→B만 가능하고 B→A는 안 될 수 있습니다. 인스타그램 팔로우처럼 — 내가 팔로우한다고 상대가 나를 팔로우하지는 않습니다.
인접행렬: 2차원 배열
노드가 5개인 그래프를 5×5 행렬로 표현합니다. matrix[i][j] = 1이면 노드 i에서 j로 간선이 있다는 뜻입니다.
A B C D
A [ 0, 1, 1, 0 ]
B [ 1, 0, 0, 1 ]
C [ 1, 0, 0, 1 ]
D [ 0, 1, 1, 0 ]A-B, A-C, B-D, C-D가 연결된 무방향 그래프입니다. 무방향이라 행렬이 대각선 기준으로 대칭입니다.
graph = [ [0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0],]
# A와 B가 연결됐는지 확인if graph[0][1] == 1: print("A-B 연결됨") # O(1)장점: 두 노드가 연결됐는지 확인하는 게 **O(1)**입니다. 인덱스로 바로 접근합니다.
단점: 노드가 N개면 N×N 크기의 배열이 필요합니다. 노드가 10,000개면 1억 개의 칸이 필요합니다. 간선이 별로 없어도 공간을 다 차지합니다.
인접리스트: 연결 목록
각 노드마다 "이 노드와 연결된 노드 목록"을 저장합니다.
A: [B, C]
B: [A, D]
C: [A, D]
D: [B, C]같은 그래프인데 표현 방식이 다릅니다.
graph = { 'A': ['B', 'C'], 'B': ['A', 'D'], 'C': ['A', 'D'], 'D': ['B', 'C'],}
# A의 이웃 노드 순회for neighbor in graph['A']: print(neighbor) # B, C장점: 간선 수에 비례하는 메모리만 씁니다. 노드가 10,000개여도 간선이 100개면 100개분의 공간만 필요합니다.
단점: 두 노드가 연결됐는지 확인하려면 리스트를 순회해야 합니다. 최악의 경우 **O(N)**입니다.
어떤 걸 써야 하나
| 인접행렬 | 인접리스트
---------+------------------+------------------
공간 | O(V^2) | O(V + E)
연결 확인 | O(1) | O(degree)
이웃 순회 | O(V) | O(degree)
적합한 경우 | 간선이 많은 밀집 그래프 | 간선이 적은 희소 그래프밀집 그래프(Dense Graph): 노드 대비 간선이 많습니다. 모든 도시 간 직항 노선이 있는 항공망. 인접행렬이 적합합니다.
희소 그래프(Sparse Graph): 노드 대비 간선이 적습니다. SNS에서 1억 명 중 각 사용자가 평균 200명만 팔로우. 인접리스트가 적합합니다.
대부분의 실무 그래프는 희소 그래프입니다. 그래서 인접리스트가 기본 선택입니다. 코딩 테스트에서도 인접리스트를 먼저 떠올리면 됩니다.
가중치 그래프
간선에 **가중치(Weight)**가 붙을 수 있습니다. 도시 간 거리, 네트워크 지연 시간 같은 것입니다.
인접행렬에서는 1 대신 가중치 값을 넣습니다:
# matrix[A][B] = 거리matrix = [ [0, 5, 3, 0], [5, 0, 0, 2], ...]인접리스트에서는 튜플로 표현합니다:
graph = { 'A': [('B', 5), ('C', 3)], 'B': [('A', 5), ('D', 2)], ...}가중치 그래프에서 최단 경로를 찾으려면 BFS 대신 다익스트라(Dijkstra) 알고리즘을 씁니다. BFS는 간선 수 기준 최단, 다익스트라는 가중치 합 기준 최단입니다.
핵심
그래프는 노드와 간선으로 다대다 관계를 표현하는 자료구조입니다. 인접행렬은 연결 확인이 O(1)이지만 공간을 O(V^2) 씁니다. 인접리스트는 공간을 O(V+E)만 쓰고, 대부분의 실무 그래프에서 기본 선택입니다.