Menu
Coddy logo textTech

Поиск в ширину (BFS)

Последнее обновление

Поиск в ширину исследует граф уровень за уровнем. Начиная с исходного узла, он сначала посещает всех его непосредственных соседей, затем всех непосещённых соседей этих узлов и так далее - расширяясь наружу кольцами возрастающего расстояния. Нажмите воспроизведение выше, чтобы увидеть, как он расходится от начального узла слой за слоем.

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

Временная и пространственная сложность

ПоказательСложностьПримечания
ВремяO(V + E)Каждая вершина и ребро посещаются один раз
ПамятьO(V)Очередь + множество посещённых, в худшем случае все узлы
ОбходУровень за уровнемБлижайшие узлы первыми, кольцами
Кратчайший путьДа (невзвешенный)Достигает каждого узла по наименьшему числу рёбер

Шаг за шагом

ШагЧто происходит
1Поместите исходный узел в очередь.
2Извлеките передний узел и отметьте его как посещённый.
3Рассмотрите каждого из его соседей.
4Поместите в очередь любого соседа, который ещё не посещён и не в очереди.
5Повторяйте, пока очередь не опустеет.

Разобранный пример

Обход этого графа от узла 0, где 0-1, 0-2, 1-3, 2-3, 2-4 - рёбра:

ШагПосещённыеОчередь (граница)
Начало{}[0]
Извлечь 0{0}[1, 2]
Извлечь 1{0, 1}[2, 3]
Извлечь 2{0, 1, 2}[3, 4]
Извлечь 3{0, 1, 2, 3}[4]
Извлечь 4{0, 1, 2, 3, 4}[] (готово)

Когда использовать BFS

Используйте, когдаИзбегайте, когда
Вам нужен кратчайший путь в невзвешенном графеУ рёбер есть веса - используйте вместо этого Dijkstra
Цель, вероятно, находится близко к источникуГраф очень широкий - очередь может хранить огромную границу
Вы хотите исследовать граф в порядке уровнейВам нужно лишь достичь любого узла, где DFS использует меньше памяти
Вы ищете компоненты связности или маршрут с минимумом переходовВам нужна топологическая сортировка или обнаружение циклов - DFS подходит лучше

Чистая, готовая к запуску реализация Breadth-First Search на Python, JavaScript, Java, C++, C. Выберите язык, скопируйте код или откройте его уже загруженным в плейграунде Coddy.

Код Breadth-First Search на Python

Python
1from collections import deque2
3
4def bfs(graph, start):5    visited = {start}6    queue = deque([start])7    order = []8    while queue:9        node = queue.popleft()10        order.append(node)11        for neighbor in graph[node]:12            if neighbor not in visited:13                visited.add(neighbor)  # mark on enqueue, not dequeue14                queue.append(neighbor)15    return order16
17
18graph = {19    "A": ["B", "C"],20    "B": ["D", "E"],21    "C": ["F"],22    "D": [],23    "E": ["F"],24    "F": [],25}26
27print("BFS order:", " -> ".join(bfs(graph, "A")))
Запустите этот код в плейграунде Python

Часто задаваемые вопросы о поиске в ширину

Какова временная сложность BFS?
BFS работает за время O(V + E), где V - число вершин, а E - число рёбер, поскольку он посещает каждую вершину один раз и рассматривает каждое ребро один раз. Он использует O(V) памяти для очереди и множества посещённых.
Находит ли BFS кратчайший путь?
Да, в невзвешенном графе. Поскольку BFS достигает узлов в порядке расстояния в переходах от источника, первый раз, когда он достигает узла, происходит по пути с наименьшим числом рёбер. Для взвешенных графов вам нужен алгоритм Dijkstra.
В чём разница между BFS и DFS?
BFS использует очередь и исследует уровень за уровнем (ближайшие первыми), тогда как DFS использует стек и уходит вглубь по одной ветви перед возвратом. BFS находит кратчайшие невзвешенные пути; DFS использует меньше памяти на широких графах и подходит для обнаружения циклов и топологической сортировки.
Когда следует использовать BFS вместо алгоритма Dijkstra?
Используйте BFS, когда все рёбра имеют одинаковую стоимость, так как он находит путь с наименьшим числом рёбер за время O(V + E) без очереди с приоритетом. Алгоритм Dijkstra нужен, когда рёбра имеют разные веса; запуск чистого BFS на взвешенном графе даёт путь с наименьшим числом переходов, а не с наименьшей стоимостью.
Зачем BFS нужно множество посещённых?
Графы могут содержать циклы, поэтому без множества посещённых BFS помещал бы один и тот же узел в очередь снова и снова и зациклился бы навсегда. Отметка узла при первом помещении в очередь (а не при извлечении) также не даёт одному и тому же узлу быть добавленным в очередь дважды разными соседями.
Отмечать узел как посещённый при помещении в очередь или при извлечении?
Отмечайте его при помещении в очередь. Если ждать извлечения, узел может быть добавлен в очередь несколько раз разными соседями до его обработки, тратя память и время. Отметка при помещении гарантирует, что каждый узел попадает в очередь ровно один раз.
Coddy programming languages illustration

Освойте алгоритмы с Coddy

НАЧАТЬ