graph = []
visited = []
def dfs(now):
# 1. 현재 노드 방문 처리
visited[now] = True
# 2. 현재 노드와 인접한 노드를 확인
for nxt in graph[now]:
# 3. 아직 방문하지 않은 노드라면 더 깊이 들어감 (재귀)
if not visited[nxt]:
dfs(nxt)
def backtracking(depth, path):
# 1. 종료 조건 (정답을 찾은 경우)
if depth == target_depth:
print(path)
return
# 2. 현재 단계에서 선택 가능한 후보들 탐색
for candidate in candidates:
# 3. 유망성 검사 (Constraint Check)
if is_promising(candidate):
# 4. 상태 변경 (선택)
visited[candidate] = True
path.append(candidate)
# 5. 다음 단계로 재귀 호출
backtracking(depth + 1, path)
# 6. 복구 (이게 백트래킹의 핵심!)
path.pop()
visited[candidate] = False
from collections import deque
def bfs(start):
q = deque()
q.append(start)
visited = set()
visited.add(start_node)
while q:
now = queue.popleft()
for nxt in graph[now]:
if nxt not in visited:
q.append(nxt)
visited.add(nxt)
def bfs(start):
visited = [start]
q = [start]
idx = 0 # 큐의 현재 위치를 가리키는 포인터
# 포인터가 큐의 전체 길이보다 작을 때까지 반복
while idx < len(q):
now = q[idx]
idx += 1
for nxt in graph[now]:
if nxt not in visited:
visited.append(nxt)
q.append(nxt)
최단 거리가 목적이 아니라 영역의 개수나 크기를 구하는 유형
count = 0
while q:
now = queue.popleft()
count += 1 # 한 칸씩 갈 때마다 카운트 증가
for nxt in graph[now]:
if nxt not in visited:
q.append(nxt)
visited.add(nxt)
# 비트마스킹 비교
if not (keys & (1 << bit)):
popleft()와 pop()이 동시에 가능하다는 점을 이용y, x → i = y*cols + xfrom collections import deque
def topological_sort(v, edges):
result = []
queue = deque()
indegree = [0] * (v + 1) # 진입 차수 배열
graph = [[] for _ in range(v + 1)]
# 1. 그래프 구축 및 진입 차수 계산
for a, b in edges:
graph[a].append(b)
indegree[b] += 1
# 2. 처음 시작할 때 진입 차수가 0인 노드를 큐에 삽입
for i in range(1, v + 1):
if indegree[i] == 0:
queue.append(i)
# 3. 큐에서 노드를 꺼내며 연결된 간선 제거
while queue:
now = queue.popleft()
result.append(now)
for neighbor in graph[now]:
indegree[neighbor] -= 1
# 새롭게 진입 차수가 0이 되면 큐에 삽입
if indegree[neighbor] == 0:
queue.append(neighbor)
# 4. 결과 확인 (방문한 노드가 V개보다 적으면 사이클이 존재한다는 뜻)
if len(result) < v:
return "Cycle Detected"
return result
def find(x):
# 경로 압축 (Path Compression)
# 루트 노드가 아니면, 루트 노드를 찾을 때까지 재귀적으로 호출하고 부모를 갱신함
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(a, b):
root_a = find(parent, a)
root_b = find(parent, b)
# 두 대표자가 다르면 하나로 합침
if root_a != root_b:
# 보통 더 작은 번호의 노드를 부모로 설정하는 규칙을 많이 씀
if root_a < root_b:
parent[root_b] = root_a
else:
parent[root_a] = root_b
return True # 성공적으로 합쳐짐
return False # 이미 같은 집합임 (사이클 발생 가능성)
def find_iterative(x):
root = x
# 1. 일단 대장(root)을 끝까지 찾아 올라감
while parent[root] != root:
root = parent[root]
# 2. 다시 x부터 올라가면서 만나는 모든 노드의 부모를 root로 통합
while parent[x] != root:
next_node = parent[x]
parent[x] = root
x = next_node
return root
import sys
# 유니온 파인드: 경로 압축(Path Compression) 적용
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(a, b):
root_a = find(parent, a)
root_b = find(parent, b)
if root_a != root_b:
parent[root_b] = root_a
return True
return False
def kruskal(v, edges):
# 1. 간선을 가중치 기준으로 오름차순 정렬
edges.sort(key=lambda x: x[2])
parent = [i for i in range(v + 1)]
mst_weight = 0
edges_count = 0
for u, v, weight in edges:
# 2. 사이클이 생기지 않는다면 간선 선택
if union(u, v):
mst_weight += weight
edges_count += 1
# 정점 - 1개의 간선을 찾으면 종료 (최적화)
if edges_count == v - 1:
break
return mst_weight
import heapq
def prim(start_node, v, adj):
visited = [False] * (v + 1)
# (가중치, 정점) 형태로 우선순위 큐 관리
pq = [(0, start_node)]
mst_weight = 0
count = 0
while pq:
weight, curr = heapq.heappop(pq)
# 이미 방문한 정점이면 무시 (사이클 방지)
if visited[curr]:
continue
visited[curr] = True
mst_weight += weight
count += 1
if count == v: # 모든 정점을 방문했다면 종료
break
# 인접한 정점 중 방문하지 않은 곳을 큐에 삽입
for next_node, next_weight in adj[curr]:
if not visited[next_node]:
heapq.heappush(pq, (next_weight, next_node))
return mst_weight