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

Depth-First Search

Поиск в глубину

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

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

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

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

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

  • Текущее состояние обхода — это путь от старта до текущей вершины; отсюда весь перебор с возвратом.
  • Три цвета вместо двух: ребро в вершину «на текущем пути» — цикл, ребро в обработанную — нет.
  • Память O(глубины), а не O(V) — но на цепочке из миллиона вершин нужно заменить явным стеком.

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

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

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

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

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

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

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

На практике

Идти вглубь, пока есть куда, и возвращаться назад только тогда, когда все соседи текущей вершины уже обработаны.

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

       A
      / \
     B   C
    / \
   D   E

A -> B -> D
       -> E
  -> C

сначала ветка B целиком (D, затем E), и только потом C
Тот же граф, что и у обхода в ширину
ШагДействие возвратов
1входим в AA
2входим в BA B
3входим в D, соседей нет — выходимA B
4входим в E, соседей нет — выходимA B
5B обработана — выходимA
6входим в C, выходим; выходим из Aпусто

Порядок выхода здесь — D, E, B, C, A. Развернув его, получаем A, C, B, E, D — корректную топологическую : каждая вершина стоит раньше тех, в кого ведут её рёбра.

WHITE, GRAY, BLACK = 0, 1, 2
color = {v: WHITE for v in graph}
order = []

def visit(v):
    color[v] = GRAY              # вершина на текущем пути
    for u in graph[v]:
        if color[u] == GRAY:
            raise CycleFound      # ребро назад -- цикл
        if color[u] == WHITE:
            visit(u)
    color[v] = BLACK             # поддерево пройдено
    order.append(v)              # порядок завершения
Рекурсивный обход и поиск цикла тремя цветами
  • Время, как и у обхода в ширину.
  • Память на возвратов, где — глубина; в худшем случае это .
  • Три цвета: белая — не тронута, серая — на текущем пути, чёрная — полностью обработана.
  • Топологическая сортировка — развёрнутый порядок завершения (order задом наперёд).

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

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

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

ВариантЧто добавляетсяЗадача
Компоненты связностивнешний цикл по непосещённымострова, группы пользователей
Поиск циклатри цветазависимости пакетов, взаимные блокировки
порядок завершениясборка, расписание, планы задач
Двудольностьраскраска в два цветаконфликты, распределение по сменам
Перебор с возвратомоткат состояния после судоку, ферзи, перестановки
Мосты и точки сочленениявремена входа и минимальные достижимыеуязвимые связи в сети
На практике

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

  • Глубина рекурсии. В Python по умолчанию около тысячи кадров; на цепочке из миллиона вершин нужен явный , а не увеличение лимита.
  • Два цвета вместо трёх. Ищете цикл — отличайте «на текущем пути» от «уже обработана», иначе цикл найдётся в любом ромбе.
  • Родитель в неориентированном графе. Ребро назад в родителя — не цикл; его нужно исключать явно.
  • Забытый откат. В переборе с возвратом состояние, изменённое перед , обязано восстанавливаться после неё.
  • Обход только из одной вершины. может быть несвязным: компоненты ищутся внешним циклом по всем вершинам.

ПриёмКогда он лучшеРазница
Обход в ширинунужен по числу шагов вместо стека
Система непересекающихся множествкомпоненты в , который растёт по рёбрампочти константа на операцию, но без путей
Алгоритм Кана с обнаружением цикла степеней входа, без
Динамикаподзадачи повторяютсяобход плюс кеш вместо чистого перебора

  1. Почему для поиска цикла нужны три цвета, а не два?
  2. Почему — это развёрнутый порядок завершения, а не порядок входа?
  3. Неориентированный : как отличить ребро в родителя от настоящего цикла?
  4. Перебор с возвратом: что произойдёт, если забыть откатить состояние?
  5. Цепочка из миллиона вершин: что сломается в рекурсивной реализации и чем её заменить?

Чем заменяют

Видеолекции

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

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

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

Graph Traversal85%

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

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

Breadth-First Search85%

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

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

Shortest Paths85%

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

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

Graph Basics85%

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

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

Centrality85%

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

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

PageRank85%

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

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

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

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

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

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