Алгоритм поиска в ширину (BFS): от теории до реализации с нуля на Python

Подробное руководство по реализации алгоритма поиска в ширину (BFS) на Python с нуля, включая базовый обход графа, поиск кратчайшего пути и практический пример с лабиринтом.
Поиск в ширину (BFS, Breadth-First Search) — это фундаментальный алгоритм обхода графа, который лежит в основе множества задач: от поиска кратчайшего пути в невзвешенном графе до анализа социальных сетей и решения головоломок. Его красота в простоте и гарантированной оптимальности для нахождения кратчайшего пути по количеству ребер. В этой статье мы разберем BFS от основных принципов до чистой реализации на Python, шаг за шагом.

В основе BFS лежит простая идея: исследовать вершины графа "слой за слоем". Начиная с исходной вершины, алгоритм сначала посещает всех ее непосредственных соседей (вершины на расстоянии 1 ребро), затем соседей этих соседей (расстояние 2 ребра) и так далее. Эта стратегия "первым пришел — первым вышел" (FIFO) естественным образом реализуется с помощью очереди (queue). Ключевые структуры данных для реализации: очередь для управления порядком обхода, множество (set) или словарь для отслеживания уже посещенных вершин (чтобы избежать циклов и повторных посещений) и, опционально, словарь для хранения информации о "родителе" каждой вершины, что позволяет восстановить путь.

Давайте реализуем BFS для графа, представленного в виде списка смежности (adjacency list). Это наиболее гибкое и распространенное представление. Предположим, наш граф ненаправленный и задан словарем, где ключ — вершина, а значение — список смежных вершин.

Шаг 1: Базовый обход. Напишем функцию, которая просто обходит граф и выводит вершины в порядке BFS.
def bfs_basic(graph, start):
 visited = set()  # Множество посещенных вершин
 queue = []  # Очередь (используем список как очередь, но эффективнее использовать collections.deque)
 visited.add(start)
 queue.append(start)
 while queue:
 vertex = queue.pop(0) # Это неэффективно для больших списков! Но для ясности.
 print(vertex, end=' ')
 for neighbor in graph[vertex]:
 if neighbor not in visited:
 visited.add(neighbor)
 queue.append(neighbor)
Эта реализация проста, но использование `queue.pop(0)` имеет линейную сложность O(n) для списка Python. В реальном коде всегда используйте `collections.deque` с его `popleft()` и `append()` операциями за O(1).

Шаг 2: Поиск кратчайшего пути и расстояний. Чаще всего BFS нужен для нахождения кратчайшего пути. Модифицируем функцию, чтобы она возвращала словарь расстояний от стартовой вершины и словарь "родителей".
from collections import deque
def bfs_shortest_path(graph, start):
 visited = {start}
 queue = deque([start])
 distance = {start: 0}  # Расстояние от start до каждой вершины
 parent = {start: None}  # Родитель вершины в BFS-дереве (для восстановления пути)
 while queue:
 vertex = queue.popleft()
 for neighbor in graph[vertex]:
 if neighbor not in visited:
 visited.add(neighbor)
 queue.append(neighbor)
 distance[neighbor] = distance[vertex] + 1
 parent[neighbor] = vertex
 return distance, parent
Теперь мы можем найти расстояние до любой достижимой вершины и восстановить сам путь.
def reconstruct_path(parent, target):
 path = []
 node = target
 while node is not None:
 path.append(node)
 node = parent.get(node) # Используем get, так как у start родитель None
 path.reverse()
 return path

Шаг 3: Решение практической задачи — поиск выхода из лабиринта. Представим лабиринт как сетку (двумерный список), где 0 — свободная клетка, 1 — стена. Нам нужно найти кратчайший путь из точки (start_row, start_col) в точку (end_row, end_col). Это классическое применение BFS на графе, где вершины — клетки, а ребра — переходы в соседние по вертикали/горизонтали клетки.
def bfs_maze(maze, start, end):
 rows, cols = len(maze), len(maze[0])
 directions = [(-1,0), (1,0), (0,-1), (0,1)] # Вверх, вниз, влево, вправо
 queue = deque()
 queue.append((start[0], start[1], 0, [start])) # (row, col, distance, path)
 visited = set()
 visited.add(start)
 while queue:
 row, col, dist, path = queue.popleft()
 if (row, col) == end:
 return dist, path # Нашли кратчайший путь и расстояние
 for dr, dc in directions:
 new_row, new_col = row + dr, col + dc
 if (0
101 5

Комментарии (7)

avatar
ths2glc 28.03.2026
Не хватает сравнения со сложностью поиска в глубину (DFS) для разных типов графов.
avatar
g2ak3q 28.03.2026
Практический пример с поиском кратчайшего пути в лабиринте был бы отличным дополнением к теории.
avatar
kaxhdauk 28.03.2026
Спасибо за статью! Начинающим программистам такие материалы очень помогают разобраться в основах.
avatar
g3mb7j19my 29.03.2026
Хорошо бы добавить визуализацию обхода графа - это сделало бы алгоритм ещё нагляднее.
avatar
mx3ehiyppxi 29.03.2026
Реализация рабочая, но для production-кода стоит добавить обработку исключений и типизацию.
avatar
pon4s74zgq 29.03.2026
Отличное объяснение! Как раз искал понятный пример реализации BFS на Python для своей учебной задачи.
avatar
v6mr3fxstdz 31.03.2026
Интересно, как можно адаптировать этот алгоритм для взвешенных графов? Есть мысли?
Вы просмотрели все комментарии