두 알고리즘은 그래프의 구조와 목적에 따라 적절하게 선택해서 사용할 수 있습니다. DFS는 노드의 깊은 부분을 빠르게 탐색하고자 할 때 유용하며, BFS는 시작 노드로부터 가까운 노드를 먼저 방문하고자 할 때 유용합니다.
깊이 우선 탐색은 시작 노드에서 시작하여 하나의 노드를 선택하고 그 노드의 인접 노드로 이동하는 방식입니다. 이를 재귀적으로 반복하면서 더 이상 방문할 노드가 없을 때까지 진행합니다.
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B'],
'F': ['C']
}
visited = set()
def dfs(graph, node):
if node not in visited:
print(node, end=' ')
visited.add(node)
for neighbour in graph[node]:
dfs(graph, neighbour)
dfs(graph, 'A') # Output: A B D E C F
너비 우선 탐색은 시작 노드에서 시작하여 그 노드의 모든 인접 노드를 먼저 방문한 후, 이웃의 이웃을 방문하는 방식으로 진행됩니다.
from collections import deque
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B'],
'F': ['C']
}
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])
bfs(graph, 'A') # Output: A B C D E F
한 정점에서 다른 정점까지의 최단 경로를 탐색합니다.
다익스트라 알고리즘은 가중치가 있는 그래프에서 시작점부터 다른 모든 정점까지의 최단 거리를 찾는 알고리즘입니다.
import heapq
def dijkstra(graph, start):
distances = {node: float('infinity') for node in graph}
distances[start] = 0
priority_queue = [(0, start)]
while priority_queue:
current_distance, current_node = heapq.heappop(priority_queue)
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node].items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
print(dijkstra(graph, 'A')) # Output: {'A': 0, 'B': 1, 'C': 3, 'D': 4}
벨만-포드 알고리즘은 다익스트라 알고리즘과 비슷한 목적을 가지지만, 음의 가중치를 가진 간선이 있는 그래프에서도 작동합니다.
def bellman_ford(graph, start):
distance = {node: float('infinity') for node in graph}
distance[start] = 0
for _ in range(len(graph) - 1):
for node in graph:
for neighbour, weight in graph[node].items():
if distance[node] + weight < distance[neighbour]:
distance[neighbour] = distance[node] + weight
# Check for negative weight cycles
for node in graph:
for neighbour, weight in graph[node].items():
if distance[node] + weight < distance[neighbour]:
print("Graph contains a negative weight cycle")
return
return distance
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
print(bellman_ford(graph, 'A')) # Output: {'A': 0, 'B': 1, 'C': 3, 'D': 4}
프림 알고리즘은 그래프의 모든 노드를 포함하면서 가중치의 합이 최소가 되는 트리, 즉 최소 신장 트리를 찾는 알고리즘입니다.
import heapq
def prim(graph, start):
mst = []
visited = set()
edges = [(0, start, start)]
while edges:
cost, frm, to = heapq.heappop(edges)
if to not in visited:
visited.add(to)
mst.append((frm, to, cost))
for to_next, cost in graph[to].items():
if to_next not in visited:
heapq.heappush(edges, (cost, to, to_next))
return mst
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
print(prim(graph, 'A')) # Output: [('A', 'B', 1), ('B', 'C', 2), ('C', 'D', 1)]
크루스칼 알고리즘도 프림 알고리즘과 같이 최소 신장 트리를 찾습니다. 하지만 이 알고리즘은 간선을 가중치 기준으로 정렬한 뒤, 가장 가중치가 작은 간선부터 추가하는 방식으로 동작합니다.
def find_parent(parents, node):
if parents[node] == node:
return node
return find_parent(parents, parents[node])
def union(parents, a, b):
root_a = find_parent(parents, a)
root_b = find_parent(parents, b)
if root_a != root_b:
parents[root_a] = root_b
def kruskal(graph):
mst = []
edges = []
for node in graph:
for neighbour, weight in graph[node].items():
edges.append((weight, node, neighbour))
edges.sort()
parents = {node: node for node in graph}
for edge in edges:
weight, frm, to = edge
if find_parent(parents, frm) != find_parent(parents, to):
mst.append((frm, to, weight))
union(parents, frm, to)
return mst
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
print(kruskal(graph)) # Output: [('A', 'B', 1), ('C', 'D', 1), ('B', 'C', 2)]
플로이드-워셜 알고리즘은 그래프의 모든 노드 쌍 사이의 최단 경로를 찾는 알고리즘입니다.
def floyd_warshall(graph):
dist = {}
for node in graph:
dist[node] = {neighbor: float('inf') for neighbor in graph}
dist[node][node] = 0
for node in graph:
for neighbor, weight in graph[node].items():
dist[node][neighbor] = weight
for k in graph:
for i in graph:
for j in graph:
if dist[i][k] + dist[k][j] < dist[i][j]:
dist[i][j] = dist[i][k] + dist[k][j]
return dist
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
print(floyd_warshall(graph))
A* 알고리즘은 휴리스틱 함수를 사용하여 경로 탐색에 있어 더 효율적인 결과를 내는 알고리즘입니다. 주로 게임에서 경로 찾기 등에 사용됩니다.
import heapq
def heuristic(a, b):
return abs(a[0] - b[0]) + abs(a[1] - b[1])
def a_star(graph, start, goal):
frontier = [(0, start)]
came_from = {start: None}
cost_so_far = {start: 0}
while frontier:
_, current = heapq.heappop(frontier)
if current == goal:
break
for neighbor in graph[current]:
new_cost = cost_so_far[current] + graph[current][neighbor]
if neighbor not in cost_so_far or new_cost < cost_so_far[neighbor]:
cost_so_far[neighbor] = new_cost
priority = new_cost + heuristic(goal, neighbor)
heapq.heappush(frontier, (priority, neighbor))
came_from[neighbor] = current
# Reconstruct path
current = goal
path = []
while current != start:
path.append(current)
current = came_from[current]
path.append(start)
path.reverse()
return path, cost_so_far[goal]
graph = {
(0, 0): {(0, 1): 1, (1, 0): 1.5},
(0, 1): {(0, 0): 1, (1, 1): 1},
(1, 0): {(1, 1): 1, (0, 0): 1.5},
(1, 1): {(1, 0): 1, (0, 1): 1}
}
print(a_star(graph, (0, 0), (1, 1))) # Output: ([(0, 0), (0, 1), (1, 1)], 2)