그래프 (Graph)
한눈에 보기
정점과 간선으로 관계를 표현하는 그래프의 기본 용어와 방향, 가중치 분류를 다룬다. 인접 행렬과 리스트의 차이, DFS와 BFS, 위상 정렬과 최단 경로 활용까지 정리했다.
최종 업데이트 2026.09.17
그래프(Graph)는 정점(Vertex)과 간선(Edge)의 집합으로 대상 사이의 연결 관계를 표현하는 비선형 자료구조다. 소셜 네트워크의 친구 관계, 도시를 잇는 도로망, 웹 페이지 사이의 링크처럼 개체가 서로 얽혀 있는 문제를 모델링할 때 그래프를 사용한다. 그래프는 인접 행렬(Adjacency Matrix) 또는 인접 리스트(Adjacency List)로 구현하며, 너비 우선 탐색(BFS)과 깊이 우선 탐색(DFS)으로 순회한다.
그래프의 기본 개념과 용어
그래프는 수학적으로 G = (V, E)로 표기한다. V는 정점의 집합이고 E는 두 정점을 잇는 간선의 집합이다. 트리(Tree)가 부모와 자식이라는 계층 관계를 강제하는 반면, 그래프는 어떤 정점이든 서로 연결될 수 있고 사이클(Cycle)도 허용한다. 사실 트리는 사이클이 없고 모든 정점이 연결된 그래프의 특수한 형태다.
면접에서 자주 등장하는 용어를 정리하면 다음과 같다.
- 차수(Degree): 한 정점에 연결된 간선의 수다. 방향 그래프에서는 들어오는 간선을 세는 진입 차수(In-degree)와 나가는 간선을 세는 진출 차수(Out-degree)로 나눈다.
- 경로(Path): 간선을 따라 한 정점에서 다른 정점으로 이동하는 정점의 나열이다.
- 사이클(Cycle): 시작 정점으로 다시 돌아오는 경로다. 사이클이 없는 방향 그래프를 DAG(Directed Acyclic Graph)라 부르며 작업 순서 결정, 즉 위상 정렬(Topological Sort)의 대상이 된다.
- 연결 그래프(Connected Graph): 임의의 두 정점 사이에 항상 경로가 존재하는 그래프다.
방향, 무방향, 가중치에 따른 분류
무방향 그래프(Undirected Graph)는 간선에 방향이 없어 (A, B) 간선이 있으면 A에서 B로도, B에서 A로도 이동할 수 있다. 친구 관계처럼 상호적인 관계를 표현한다. 방향 그래프(Directed Graph)는 간선 <A, B>가 A에서 B로 가는 한 방향만 의미한다. 팔로우 관계나 작업의 선후 관계가 대표적인 예다.
가중치 그래프(Weighted Graph)는 간선마다 비용이나 거리 같은 값을 부여한 그래프다. 도시 사이의 거리, 네트워크 전송 비용을 표현할 때 쓰며, 다익스트라(Dijkstra) 최단 경로 알고리즘이나 최소 신장 트리(Minimum Spanning Tree)를 구하는 크루스칼(Kruskal), 프림(Prim) 알고리즘이 가중치 그래프 위에서 동작한다.
인접 행렬과 인접 리스트
인접 행렬은 정점 수 V에 대해 V x V 크기의 2차원 배열을 만들고, 정점 i에서 j로 가는 간선이 있으면 matrix[i][j]에 1 또는 가중치를 저장하는 방식이다. 두 정점의 연결 여부를 O(1)에 확인할 수 있지만, 간선 유무와 무관하게 항상 O(V^2)의 공간을 차지한다.
인접 리스트는 정점마다 연결된 이웃 정점의 목록을 저장하는 방식이다. 공간을 O(V + E)만 사용하므로 실제 그래프처럼 간선이 드문 희소 그래프(Sparse Graph)에 유리하다. 다만 특정 간선의 존재 여부를 확인하려면 해당 정점의 리스트를 따라가야 하므로 최악에 O(V)가 걸린다.
| 연산 | 인접 행렬 | 인접 리스트 |
|---|---|---|
| 공간 | O(V^2) | O(V + E) |
| 간선 (i, j) 존재 확인 | O(1) | O(차수), 최악 O(V) |
| 정점 i의 모든 이웃 순회 | O(V) | O(차수) |
| 간선 추가 | O(1) | O(1) |
| 유리한 경우 | 밀집 그래프, 간선 조회가 잦을 때 | 희소 그래프, 순회가 잦을 때 |
파이썬(Python)으로 두 방식을 표현하면 다음과 같다.
# 정점 4개, 간선: 0-1, 0-2, 1-3 (무방향)
# 인접 행렬
matrix = [
[0, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 0],
[0, 1, 0, 0],
]
# 인접 리스트
adj = {
0: [1, 2],
1: [0, 3],
2: [0],
3: [1],
}
그래프 순회: BFS와 DFS
너비 우선 탐색(BFS, Breadth-First Search)은 시작 정점에서 가까운 정점부터 층층이 방문하며, 큐(Queue)를 사용한다. 간선의 가중치가 모두 같을 때 최단 경로를 찾는 용도로 쓴다. 깊이 우선 탐색(DFS, Depth-First Search)은 한 방향으로 끝까지 내려간 뒤 되돌아오며, 재귀 호출이나 스택(Stack)으로 구현한다. 사이클 탐지, 연결 요소 찾기, 위상 정렬에 활용한다.
그래프는 트리와 달리 사이클이 있을 수 있으므로, 방문한 정점을 기록하는 visited 배열이 없으면 무한 루프에 빠진다. 이 점이 트리 순회와 그래프 순회의 결정적인 차이다.
from collections import deque
def bfs(adj, start):
visited = {start}
queue = deque([start])
while queue:
node = queue.popleft()
print(node)
for nxt in adj[node]:
if nxt not in visited:
visited.add(nxt)
queue.append(nxt)
def dfs(adj, node, visited=None):
if visited is None:
visited = set()
visited.add(node)
print(node)
for nxt in adj[node]:
if nxt not in visited:
dfs(adj, nxt, visited)
두 순회 모두 모든 정점을 한 번씩 방문하고 모든 간선을 상수 번 확인하므로, 인접 리스트 기준 시간 복잡도는 O(V + E)다. 인접 행렬로 구현하면 각 정점에서 V개의 칸을 모두 확인해야 하므로 O(V^2)가 된다.
면접에서 이렇게 물어본다
- 인접 행렬과 인접 리스트의 공간 복잡도와 연산별 시간 복잡도를 비교하고, 어떤 상황에서 무엇을 선택할지 설명해 보라.
- BFS와 DFS의 동작 방식과 사용하는 자료구조를 설명하고, 각각 어떤 문제에 적합한지 예를 들어 보라.
- 그래프와 트리의 차이는 무엇이며, 트리는 그래프의 어떤 특수한 경우인지 설명해 보라.
- 가중치가 없는 그래프에서 최단 경로를 구할 때 DFS가 아니라 BFS를 쓰는 이유를 설명해 보라.
- 방향 그래프에서 사이클을 탐지하는 방법을 설명해 보라.
자료구조에서 같이 읽기
- 배열 (Array)같은 타입의 데이터를 연속 메모리에 저장하는 기본 선형 자료구조인 배열을 다룬다. O(1) 인덱스 접근의 원리, 연산별 시간 복잡도, 동적 배열의 확장과 분할 상환 분석, 캐시 지역성까지 정리했다.
- Array vs ArrayList vs LinkedList배열, ArrayList, LinkedList의 내부 저장 방식과 연산별 시간 복잡도를 비교한다. 캐시 지역성 때문에 실무에서 ArrayList가 유리한 이유와 상황별 선택 기준을 정리했다.
- AVL 트리와 레드 블랙 트리 (AVL Tree & Red-Black Tree)자가 균형 이진 탐색 트리인 AVL 트리와 레드 블랙 트리를 비교한다. 균형 인수와 회전, 탐색과 삽입 비용, TreeMap이 레드 블랙 트리를 쓰는 이유를 정리했다.
- B 트리와 B+ 트리 (B-Tree & B+ Tree)한 노드에 여러 키를 담아 높이를 낮춘 B 트리와 리프에 데이터를 모아 연결한 B+ 트리를 다룬다. 디스크 I/O 최소화 원리와 데이터베이스 인덱스가 B+ 트리를 쓰는 이유를 정리했다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기