두 알고리즘은 그래프의 구조와 목적에 따라 적절하게 선택해서 사용할 수 있습니다. 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)