Чем обход в ширину (BFS) отличается от обхода в глубину (DFS)?
- Как найти кратчайший путь в невзвешенном графе?
- Когда использовать DFS?
- Как найти связные компоненты графа?
BFS идёт по уровням через очередь и находит кратчайший путь по числу рёбер; DFS идёт вглубь до упора через стек или рекурсию.
- Оба O(V + E), оба отмечают посещённые вершины
- BFS: кратчайший путь в невзвешенном графе, уровни, ближайшие объекты
- DFS: циклы, топологическая сортировка, связные компоненты, перебор с возвратом
А ещё Для графов с весами BFS не подходит — нужна Дейкстра
Подробно
Обход в ширину (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) и отметка посещённых.
Частые ошибки
- Искать кратчайший путь обходом в глубину.
- Не отмечать посещённые вершины и зациклиться.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗BFS идёт вглубь по стеку и находит кратчайший путь во взвешенном графе; DFS идёт по уровням через очередь и нужен для поиска ближайших соседей
- ✗BFS находит кратчайший путь во взвешенном графе
- ✗DFS всегда находит кратчайший путь по числу рёбер
- ✓BFS идёт по уровням через очередь и находит кратчайший путь по числу рёбер; DFS идёт вглубь через стек или рекурсию
Что спросят дальше
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.