Алгоритмы и структуры данных

Breadth-First Search

Поиск в ширину

актуальноТекущий рабочий стандарт

Часть темы: Graph TraversalОбходы графа: семейство

Направления: Основы · Алгоритмы и структуры данных

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

Расстояние до соседа на единицу больше — на этом держится послойный порядок

Ключевые тезисы

  • по числу рёбер — и только по числу: разные веса ломают корректность, а не скорость.
  • Вершина помечается при добавлении в , иначе очередь раздувается до O(E).
  • Мультистарт, 0-1 и обход по состояниям — те же десять строк с другим содержимым .

Какую задачу решает

«За сколько ходов», «минимальное число замен», «сколько шагов до выхода» — задачи, где путей экспоненциально много, а нужен кратчайший. Перебор путей безнадёжен, а находит какой-нибудь путь и выдаёт его за ответ.

устроен так, что получается сам собой: вершины обрабатываются слоями, и первое посещение любой вершины оказывается кратчайшим — при условии, что все переходы стоят одинаково. Это условие и есть единственная граница применимости приёма.

Подробный разбор

10 подтем — объяснения, формулы, примеры и интерактивные графики.

«За сколько ходов конь дойдёт до клетки», «какое минимальное число замен превращает одно слово в другое», «сколько шагов до выхода из лабиринта» — во всех случаях путей экспоненциально много, и перебирать их бессмысленно.

задачу тоже не решает: он найдёт какой-нибудь путь, но не кратчайший, — и это самая частая подмена на собеседовании, потому что на маленьких тестах результат часто совпадает.

На практике

Обходить вершины слоями: сначала все, до которых один шаг, потом все, до которых два. Тогда первое посещение вершины и есть до неё.

Дисциплина «первым пришёл — первым вышел» держит этот порядок сама: никогда не отдаст вершину дальнего слоя раньше, чем закончится ближний.

       A
      / \
     B   C
    / \
   D   E

слой 0:  A
слой 1:  B C
слой 2:  D E
Граф из пяти вершин
ШагИзвлеклиДобавили в после шагаРасстояния
1AB, CB CA = 0, B = 1, C = 1
2BD, EC D ED = 2, E = 2
3CD Eбез изменений
4DEбез изменений
5Eпустообход завершён

Вершины B и C помечены посещёнными в момент добавления в , а не в извлечения. Поэтому на третьем шаге C не добавляет соседей повторно и очередь не раздувается.

from collections import deque

def bfs(graph, start):
    dist = {start: 0}
    parent = {start: None}
    queue = deque([start])

    while queue:
        v = queue.popleft()
        for u in graph[v]:
            if u in dist:            # уже посещена или уже в очереди
                continue
            dist[u] = dist[v] + 1
            parent[u] = v            # для восстановления пути
            queue.append(u)

    return dist, parent
Обход в ширину с расстояниями и восстановлением пути
  • Время: каждая вершина извлекается один раз, каждое ребро просматривается один раз.
  • Память: в в худшем случае лежит целый слой.
  • Предусловие — стоимость всех переходов одинакова; иначе это Дейкстра.
  • Путь восстанавливается по parent — от цели к старту, затем разворачивается.

Индукция по слоям. Пусть все вершины на расстоянии уже извлечены в порядке и получили верные расстояния. Их соседи, ещё не помеченные, находятся ровно на расстоянии : ближе они быть не могут (иначе были бы помечены раньше), дальше — тоже (есть путь через соседа). отдаёт их только после всех вершин слоя , значит порядок сохраняется и на следующем слое.

Отсюда же правило пометки. Если помечать вершину при извлечении, она успеет попасть в столько раз, сколько у неё соседей: на плотном очередь раздувается до вместо . Обход останется корректным, но память вырастет — и на разреженных тестах это незаметно, поэтому ошибка доживает до продакшена.

На практике

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

Сигнал в условииЧто применять
«Минимальное число шагов, ходов, замен, переходов»
Все переходы равноценны
Лабиринт, сетка, «за сколько распространится»обход по сетке, часто мультистарт
«Все заражённые распространяются одновременно»мультистарт: все источники в сразу
Переходы стоят по-разномуне , а
Нужен любой путь или все пути

ВариантЧто меняетсяЗадача
По сеткесоседи — четыре или восемь направленийлабиринт, острова, заливка
Мультистартв кладутся все источники сразугниющие апельсины, расстояние до ближайшей воды
0-1 дек: ребро веса 0 — в начало, веса 1 — в конецминимум переключений, стен, пересадок
По состояниямвершина — кортеж, а не точкапозиция плюс собранные ключи или остаток топлива
Двунаправленныйдве волны навстречуогромный с известной целью
Алгоритм Кана вершин с нулевой степенью входа

  • Пометка при извлечении вместо добавления. раздувается; на плотном это переполнение памяти.
  • Разные веса рёбер. даст неверный ответ — молча.
  • Неполный ключ состояния. Если вершина — кортеж, в посещённые кладётся кортеж целиком, иначе обход срежет правильный путь.
  • Границы сетки. Проверять их нужно до обращения к клетке, а не после.
  • Список вместо очереди. list.pop(0) в Python стоит — обход становится квадратичным без единой ошибки в логике.

ПриёмКогда он лучшеРазница
Обход в глубинунужна связность, циклы, все вариантыне даёт кратчайшего пути
Дейкстрау переходов разная стоимость вместо обычной
0-1 веса только 0 и 1дек вместо кучи, остаётся
A*цель известна и есть оценка расстоянияэвристика сокращает перебор
Двунаправленный поиск ветвится сильно, цель известнадве волны вместо одной

  1. Почему вершину нужно помечать при добавлении в , а не при извлечении?
  2. , где все рёбра весят 1, кроме одного веса 5: что вернёт и почему это неверно?
  3. Мультистарт: чем расстояния при нескольких источниках отличаются от расстояний от одного?
  4. Лабиринт с ключами и дверями: что здесь является вершиной?
  5. Как восстановить сам путь, а не только его длину?

Чем заменяют

Видеолекции

Тайм-кода именно на эту тему в записях нет. Но глава «Алгоритмы и структуры данных» разобрана в курсе целиком — с той оговоркой, что место в записи придётся искать самому.

Связанные темы

Алгоритмы на графах

Graph Traversal85%

Обходы графа: семейство · Алгоритмы и структуры данных

Зонтичная тема: что общего у обоих обходов, как увидеть граф в задаче, где он не назван, и по каким двум вопросам выбирается обход. Сами обходы разобраны отдельно.

Depth-First Search85%

Поиск в глубину · Алгоритмы и структуры данных

Идти вглубь до упора и возвращаться. Порядок завершения вершин даёт топологическую сортировку, три цвета — обнаружение циклов, откат состояния — перебор с возвратом.

Shortest Paths85%

Кратчайшие пути · Алгоритмы и структуры данных

Дейкстра для неотрицательных весов, Беллман — Форд для отрицательных, A* — когда есть оценка расстояния до цели.

Graph Basics85%

Основы графов · Графы и сети

Вершины, рёбра, веса и направления. Матрица смежности и список рёбер — два способа хранить одно и то же.

Centrality85%

Центральности · Графы и сети

Меры важности вершины: по числу связей, по посредничеству, по близости и по влиянию соседей.

PageRank85%

PageRank · Графы и сети

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

Проверить себя

Тема встречается в тесте по направлениям — ошибки приведут обратно на эту страницу.

Следующий шагПроверить себя: Алгоритмы и структуры данныхТема встречается в этом тесте 1 раз. Ошибка приведёт обратно на эту страницу — с объяснением, что именно не сошлось.Перейти →

Глава «Алгоритмы и структуры данных» последний раз правилась . Нашли ошибку — напишите.