그래프 표현 방법: 인접 행렬(Adjacency Matrix)과 인접 리스트(Adjacency List) 비교

그래프는 컴퓨터 과학에서 매우 중요한 자료구조로, 데이터 간의 관계를 효과적으로 표현할 수 있다.

그래프를 표현하는 방법을 선택할 때는 해결하려는 문제의 특성과 그래프의 구조를 고려해야 한다.

  • 간선이 적은 희소 그래프의 경우 인접 리스트가 메모리와 성능 면에서 우수
  • 간선이 많은 밀집 그래프나 정점 간 연결 여부를 빠르게 확인해야 하는 경우에는 인접 행렬이 적합하다.
    실제로는 두 방법을 혼합하거나 응용한 자료구조를 사용하기도 한다.

많은 실제 응용 사례(소셜 네트워크, 웹 페이지 연결 등)에서는 정점 수에 비해 간선 수가 적은 희소 그래프의 특성을 가지므로 인접 리스트가 더 많이 사용되는 경향이 있다.

인접 행렬(Adjacency Matrix)

인접 행렬은 2차원 배열을 사용하여 그래프의 연결 관계를 표현하는 방법.

기본 개념

  • n개의 정점이 있다면 n×n 크기의 2차원 배열을 생성한다.
  • 행렬의 각 원소 M[i][j]는 정점 i에서 정점 j로 가는 간선이 있으면 1(또는 가중치), 없으면 0을 저장한다.
  • 무방향 그래프의 경우 인접 행렬은 대칭 행렬이 된다.

인접 행렬의 예시

무방향 그래프 (Undirected Graph)
1
2
3
  A — B
  |   |
  C — D

인접 행렬 표현

ABCD
A0110
B1001
C1001
D0110
방향 그래프 (Directed Graph)
1
2
3
  A → B
  C → D

인접 행렬 표현

ABCD
A0110
B0000
C0001
D0000

구현 예시 (Python)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class GraphMatrix:
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        # 모든 값을 0으로 초기화한 2차원 배열 생성
        self.matrix = [[0 for _ in range(num_vertices)] for _ in range(num_vertices)]
    
    def add_edge(self, v1, v2, weight=1):
        # 정점 v1에서 v2로 가는 간선 추가
        self.matrix[v1][v2] = weight
        
        # 무방향 그래프인 경우 양방향 연결
        # self.matrix[v2][v1] = weight
        
    def remove_edge(self, v1, v2):
        # 정점 v1에서 v2로 가는 간선 제거
        self.matrix[v1][v2] = 0
        
        # 무방향 그래프인 경우 양방향 연결 제거
        # self.matrix[v2][v1] = 0
        
    def print_graph(self):
        for row in self.matrix:
            print(row)

인접 행렬이 유리한 경우

  1. 그래프의 간선 밀도가 높은 경우 (밀집 그래프)
  2. 두 정점 간 간선 존재 여부를 자주 확인해야 하는 경우
  3. 가중치 정보를 쉽게 표현하고 접근해야 하는 경우
  4. 알고리즘이 행렬 연산을 기반으로 하는 경우 (예: 플로이드-워셜 알고리즘)

인접 행렬 활용 분야

  • 플로이드-워셜 알고리즘 (모든 정점 간 최단 경로)
  • 네트워크 연결 상태 분석
  • 이미지 및 그래픽 처리 (픽셀 연결성 분석)
  • 완전 그래프(Complete Graph)처럼 간선이 많은 경우

인접 리스트(Adjacency List)

인접 리스트는 각 정점마다 해당 정점과 인접한 정점들의 리스트를 저장하는 방법이다.

기본 개념

  • 모든 정점에 대해 해당 정점과 연결된 다른 정점들의 리스트를 유지한다.
  • 정점 수만큼의 리스트 배열이 필요하다.
  • 가중치 그래프의 경우, 연결된 정점과 함께 가중치도 저장한다.

인접 리스트의 예시

무방향 그래프 (Undirected Graph)
1
2
3
  A — B
  |   |
  C — D

인접 리스트 표현

1
2
3
4
A: B, C
B: A, D
C: A, D
D: B, C
방향 그래프 (Directed Graph)
1
2
3
  A → B
  C → D

인접 리스트 표현

1
2
3
4
A: B, C
B: 
C: D
D:

구현 예시 (Python)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class GraphList:
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        # 각 정점마다 빈 리스트 생성
        self.adj_list = [[] for _ in range(num_vertices)]
    
    def add_edge(self, v1, v2, weight=1):
        # 정점 v1에서 v2로 가는 간선 추가 (가중치 포함)
        self.adj_list[v1].append((v2, weight))
        
        # 무방향 그래프인 경우 양방향 연결
        # self.adj_list[v2].append((v1, weight))
        
    def remove_edge(self, v1, v2):
        # v1에서 v2로 가는 간선 제거
        self.adj_list[v1] = [(vertex, weight) for vertex, weight in self.adj_list[v1] if vertex != v2]
        
        # 무방향 그래프인 경우 v2에서 v1로 가는 간선도 제거
        # self.adj_list[v2] = [(vertex, weight) for vertex, weight in self.adj_list[v2] if vertex != v1]
        
    def print_graph(self):
        for i in range(self.num_vertices):
            print(f"정점 {i}:", end=" ")
            for j, weight in self.adj_list[i]:
                print(f"({j}, 가중치: {weight})", end=" ")
            print()

인접 리스트가 유리한 경우

  1. 그래프의 간선 밀도가 낮은 경우 (희소 그래프)
  2. 메모리 사용량이 중요한 경우
  3. 모든 간선을 순회해야 하는 경우
  4. 특정 정점에서 출발하는 모든 간선을 자주 찾아야 하는 경우
  5. 그래프 탐색 알고리즘(BFS, DFS)을 구현할 때

인접 리스트 활용 분야

  • 다익스트라 알고리즘 (최단 경로 탐색)
  • 소셜 네트워크 분석 (Facebook, Twitter)
  • 추천 시스템 (Netflix, YouTube)
  • 네트워크 라우팅 (인터넷 패킷 경로 탐색)
  • 웹 크롤링 (페이지 간 링크 분석)

인접 행렬과 인접 리스트 비교

특성인접 행렬 (Adjacency Matrix)인접 리스트 (Adjacency List)
메모리 사용량O(V²) - 정점 수의 제곱에 비례O(V + E) - 정점 수와 간선 수의 합에 비례
간선 존재 확인O(1) - 상수 시간O(degree(V)) - 정점의 차수에 비례
모든 간선 순회O(V²) - 행렬 전체 순회 필요O(V + E) - 모든 정점과 간선 순회
특정 정점의 모든 인접 정점 찾기O(V) - 해당 행 전체 확인 필요O(degree(V)) - 해당 정점의 리스트만 확인
간선 추가O(1) - 배열 원소 변경O(1) - 리스트에 추가
간선 제거O(1) - 배열 원소 변경O(degree(V)) - 리스트에서 검색 후 제거
그래프 밀집도에 따른 효율성밀집 그래프에 효율적희소 그래프에 효율적
구현 복잡도간단함약간 복잡함
공간 효율성희소 그래프에서 비효율적메모리 사용이 최적화됨
특정 두 정점 간 연결 확인O(1) - 즉시 확인 가능O(degree(V)) - 리스트 검색 필요
그래프 순회 알고리즘 구현복잡하고 느림간단하고 빠름
적합한 그래프 유형작은 그래프, 완전 그래프큰 그래프, 희소 그래프
웹 그래프와 같은 대규모 그래프비실용적실용적
동적 그래프(변화가 많은)비효율적효율적

참고 및 출처