Обход графа: BFS и DFS

Способы представления графа, обход в ширину и в глубину, поиск кратчайшего пути в невзвешенном графе, проверка связности и циклов.

Как хранить граф

СпособПамятьПроверка ребраКогда применять
Матрица смежностиO(n²)O(1)Плотный граф, малое n
Список смежностиO(n + m)O(степень вершины)Разреженный граф — почти всегда
Список рёберO(m)O(m)Алгоритмы Крускала, Форда-Беллмана
# список смежности
g = {
    1: [2, 3],
    2: [1, 4],
    3: [1, 4],
    4: [2, 3, 5],
    5: [4],
}

BFS — обход в ширину

Идём по уровням: сначала все соседи, потом соседи соседей. Реализуется очередью. Даёт кратчайший путь в невзвешенном графе — по количеству рёбер.

from collections import deque

def bfs(g, start):
    visited = {start}
    dist = {start: 0}
    q = deque([start])
    while q:
        v = q.popleft()
        for u in g[v]:
            if u not in visited:
                visited.add(u)
                dist[u] = dist[v] + 1
                q.append(u)
    return dist   # расстояния в рёбрах от start

DFS — обход в глубину

Идём вглубь до упора, затем откатываемся. Реализуется рекурсией или стеком. Используется для поиска компонент связности, топологической сортировки, поиска циклов.

def dfs(g, v, visited=None):
    if visited is None:
        visited = set()
    visited.add(v)
    for u in g[v]:
        if u not in visited:
            dfs(g, u, visited)
    return visited

# итеративная версия — не переполняет стек на больших графах
def dfs_iter(g, start):
    visited, stack = set(), [start]
    while stack:
        v = stack.pop()
        if v in visited:
            continue
        visited.add(v)
        stack.extend(u for u in g[v] if u not in visited)
    return visited

Типовые задачи на основе обходов

Если у рёбер есть веса, BFS кратчайший путь уже не даёт — нужен Дейкстра (неотрицательные веса) или Форд-Беллман (есть отрицательные).

Сложность

Оба обхода — O(n + m) на списках смежности и O(n²) на матрице смежности, где n — вершины, m — рёбра. Каждая вершина и каждое ребро обрабатываются один раз.

Частые вопросы

Чем BFS лучше DFS?

BFS находит кратчайший путь по числу рёбер и не рискует переполнить стек. DFS проще в записи и естественнее для задач на связность и топологическую сортировку.

Как восстановить сам путь, а не длину?

Заводите словарь parent: при первом посещении вершины u из v пишете parent[u] = v. В конце идёте от финиша к старту и разворачиваете список.

Читайте также

Сделаем работу по этой теме

Опишите задачу — ответим в течение 15 минут в личных сообщениях ВКонтакте, назовём срок и цену. Предоплаты за оценку нет.

  • Оценка заявки бесплатно
  • Правки по замечаниям преподавателя
  • Работы по всем техническим и IT-дисциплинам

Нажимая кнопку, вы соглашаетесь на обработку указанных данных для ответа на заявку.