Получить доступ

Чем обход в ширину (BFS) отличается от обхода в глубину (DFS)?

JuniorГрафы
Ещё спрашивают так:
  • Как найти кратчайший путь в невзвешенном графе?
  • Когда использовать DFS?
  • Как найти связные компоненты графа?
Коротко

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

  • Оба O(V + E), оба отмечают посещённые вершины
  • BFS: кратчайший путь в невзвешенном графе, уровни, ближайшие объекты
  • DFS: циклы, топологическая сортировка, связные компоненты, перебор с возвратом

А ещё Для графов с весами BFS не подходит — нужна Дейкстра

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

Поиск пути на сетке: BFS, DFS, Дейкстра, A*, жадный

Подробно

Обход в ширину (BFS). Начинаем со стартовой вершины, кладём её в очередь. Пока очередь не пуста: достаём вершину, добавляем в очередь всех её непосещённых соседей. Так сначала посещаются все вершины на расстоянии 1, потом на расстоянии 2 и так далее — «волной». Поэтому первый раз, когда BFS дошёл до вершины, он дошёл по кратчайшему пути (по числу рёбер). Запоминая родителя каждой вершины, путь можно восстановить.

Обход в глубину (DFS). Идём от вершины к соседу, от него — к его соседу, пока не упрёмся; затем возвращаемся и пробуем другие ветки. Реализуется рекурсией или явным стеком. DFS не находит кратчайший путь, но удобен, когда важна структура графа:

  • найти циклы;
  • топологическая сортировка (порядок сборки зависимостей);
  • связные компоненты и компоненты сильной связности;
  • перебор с возвратом (backtracking) — лабиринты, головоломки.

Общее: оба алгоритма проходят каждую вершину и каждое ребро по разу — O(V + E), и обязаны отмечать посещённые вершины, иначе на графе с циклами зациклятся. Память: BFS хранит «фронт» волны (может быть широким), DFS — путь от корня (может быть глубоким; рекурсия на длинном пути переполнит стек вызовов).

Пример

from collections import deque

def shortest_path(graph, start, goal):
    parent = {start: None}
    queue = deque([start])
    while queue:
        v = queue.popleft()
        if v == goal:
            path = []
            while v is not None:
                path.append(v)
                v = parent[v]
            return path[::-1]
        for u in graph[v]:
            if u not in parent:          # посещённые отмечены родителем
                parent[u] = v
                queue.append(u)
    return None

Что хотят услышать

  • Механика: очередь против стека или рекурсии.
  • BFS даёт кратчайший путь в невзвешенном графе.
  • Задачи, где удобнее DFS.
  • Сложность O(V + E) и отметка посещённых.

Частые ошибки

  • Искать кратчайший путь обходом в глубину.
  • Не отмечать посещённые вершины и зациклиться.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем