-
그래프의 기본
G =(V, E)
- 그래프 G는 정점(Vertex)와 간선(Edge)으로 구성된다
- V: 정점(vertex)의 집합
- E: 간선(edge)의 집합
그래프 구현 방법
인접 행렬
- 정점을 2차원 배열로 표현
- 행과 열에 해당하는 두 정점 사이에 간선이 있으면 1(또는 가중치), 없으면 0
- 공간복잡도 O(V^2) 정점수가 많으면 메모리 낭비
- 특정 두 정점간 연결 여부를 O(1)로 확인
정점: 0, 1, 2 간선: (0-1), (1-2) 인접 행렬: [ [0, 1, 0], [1, 0, 1], [0, 1, 0] ]
인접 리스트
- 각 정점마다 연결된 정점 리스트를 별도로 저장
- 공간복잡도 O(V + E) (간선 수 만큼 메모리 사용 > 희소 그래프에 적합)
- 정점마다 연결된 간선만 확인해 효율적임
0: [1] 1: [0, 2] 2: [1]
그래프의 순회
깊이 우선 탐색 알고리즘 (DFS)
- 한 정점에서 시작해서 가능한 깊게 탐색하다가 더 이상 진행할 수 없으면 다시 되돌아오면서 탐색하는 방식
- 스택(Stack) 자료구조를 사용해 구현하거나, 재귀 호출로 구현할 수 있음
처리과정
- 시작 정점을 스택에 넣고 방문 표시
- 스택에서 정점을 꺼낸 후
- 아직 방문하지 않은 인접 정점을 스택에 추가
- 스택이 빌 때까지 반복
def dfs(graph, start): visited = set() stack = [start] while stack: node = stack.pop() if node not in visited: print(node, end=' ') visited.add(node) # 인접 정점을 스택에 추가 (뒤에서 꺼내기 때문에 reversed) stack.extend(reversed(graph[node])) # 예시 graph = { 0: [1, 2], 1: [0, 3], 2: [0, 4], 3: [1], 4: [2] } dfs(graph, 0) # 0 1 3 2 4
너비 우선 탐색 알고리즘 (BFS)
- 시작 정점에서 가까운 정점부터 차례로 탐색하는 방식
- 큐(Queue) 자료구조를 사용해 구현
처리과정
- 시작 정점을 큐에 넣고 방문 표시
- 큐에서 정점을 꺼내고
- 아직 방문하지 않은 인접 정점을 큐에 추가
- 큐가 빌 때까지 반복
from collections import deque def bfs(graph, start): visited = set() queue = deque([start]) while queue: node = queue.popleft() if node not in visited: print(node, end=' ') visited.add(node) queue.extend(graph[node]) # 예시 graph = { 0: [1, 2], 1: [0, 3], 2: [0, 4], 3: [1], 4: [2] } bfs(graph, 0) # 0 1 2 3 4
그래프 순회 응용
위상 정렬(Topological Sort)
개념
- 사이클이 없는 방향 그래프(DAG) 에서 모든 간선의 방향을 거스르지 않게 정점들을 나열하는 방법
- 주로 선행 조건이 있는 작업들의 실행 순서를 결정할 때 사용
특징
- 여러 가지 정렬 결과가 있을 수 있음 (정답이 하나만 존재하지 않음)
- DFS 또는 진입차수(Indegree) 기반 방법으로 구현
과정 (DFS 기반)
- DFS를 수행한다
- 모든 자식 정점(선행해야 할 작업) 을 다 방문한 후,
- 즉, 더 이상 방문할 곳이 없을 때, 해당 정점을 스택에 삽입
- (단순 방문순서가 아니라, 선행작업이 다 끝난 뒤 스택에 push하는 것)
- DFS가 끝난 후 스택을 역순으로 꺼내면 위상 정렬 결과가 됨
- 이 순서는 각 정점이 선행 조건을 모두 충족한 상태에서 수행할 수 있는 순서
예시
5 → 2 → 3 → 1 4 → 0 4 → 1 5 → 0
구분 내용 DFS 방문 순서 5 → 2 → 3 → 1 → 0 → 4 DFS 순서에 따라 스택에 쌓인 순서 (push 순서) 1 → 3 → 2 → 0 → 5 → 4 스택에서 pop한 순서 (위상 정렬 결과) 4 → 5 → 0 → 2 → 3 → 1 연결 성분(Connected Component)
- 무방향 그래프에서 임의의 두 정점 사이에 경로가 존재하는 최대 부분 그래프
- DFS 또는 BFS로 연결된 모든 정점을 찾으면 하나의 연결 성분
과정
- 전체 정점을 돌면서 방문하지 않은 정점에 대해 탐색 시작
- 탐색을 마치면 하나의 연결 성분 완성
- 모든 정점을 처리할 때까지 반복
def connected_components(graph): visited = set() components = [] def dfs(node, component): visited.add(node) component.append(node) for neighbor in graph[node]: if neighbor not in visited: dfs(neighbor, component) for node in graph: if node not in visited: component = [] dfs(node, component) components.append(component) return components # 예시 graph = { 0: [1], 1: [0], 2: [3], 3: [2], 4: [] } print(connected_components(graph)) # [[0, 1], [2, 3], [4]]
강연결 성분(Strongly Connected Component, SCC)
- 방향 그래프에서 임의의 두 정점 사이에 양방향 경로가 존재하는 최대 부분 그래프
과정
- 원래 그래프에서 DFS를 수행하여 방문 완료 순서를 기록
- 그래프의 모든 간선을 반전(Transpose)한 새로운 그래프를 만든다
- 방문 완료 순서의 역순으로 반전 그래프에서 DFS를 다시 수행
- DFS를 수행할 때 방문하는 정점들이 하나의 강연결 성분을 이룸
예시
1 → 2 → 3 → 1 4 → 5 5 → 6 → 4
여기서 강연결 성분은:
- {1, 2, 3} (서로 서로 갈 수 있음)
- {4, 5, 6} (4→5→6→4 순환)
- (나머지 단독 정점들은 자기 혼자 강연결 성분)
'알고리즘' 카테고리의 다른 글
그래프(3) - 벨만-포드, 플로이드, 포드-풀커슨 (0) 2025.05.04 그래프(2) - 크루스칼, 프림, 데이크스트라 알고리즘 (0) 2025.04.29 탐색&해싱 (0) 2025.04.24 정렬(3) - 힙, 계수, 기수, 버킷 (0) 2025.03.27 정렬(2) - 퀵정렬, 합병정렬 (0) 2025.03.23 댓글