Использование обхода в глубину для поиска цикла — различия между версиями
(пункты 2, 5 - 11) |
(пункты 1, 3, 4) |
||
Строка 1: | Строка 1: | ||
− | + | {{Задача | |
− | + | |definition = Дан граф, требуется проверить наличие [[Основные определения теории графов|цикла]] в этом графе. Будем решать задачу с помощью [[Обход в глубину, цвета вершин|поиска в глубину]] за <tex>O(|V| + |E|)</tex>. | |
− | + | }} | |
== Алгоритм == | == Алгоритм == | ||
− | В случае <b>ориентированного графа</b> | + | В случае <b>ориентированного графа</b> произведём серию поисков в глубину. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе {{---}} в чёрный. И, если поиск в глубину пытается пойти в серую вершину, то это означает, что цикл найден. |
− | В случае <b>неориентированного графа</b> | + | В случае <b>неориентированного графа</b>, используя указанный алгоритм, мы перейдем по какому-либо ребру в обратном направлении и попадем в серую вершину, что будет означать наличие цикла. Проблема в том, что по определению [[Основные определения теории графов|цикла]] в неориентированном графе, одно и то же ребро не может содержаться в цикле дважды. Чтобы избежать этого, необходимо передавать еще один параметр поиска в глубину {{---}} вершину, из которой мы пришли. Теперь можно считать, что цикл найден, если вершина, в которую мы хотим пойти серая и не является вершиной, из которой мы пришли. |
Для восстановления самого цикла достаточно при запуске поиска в глубину из очередной вершины добавлять эту вершину в стек. Когда поиск в глубину нашел вершину, которая лежит на цикле, будем последовательно вынимать вершины из стека, пока не встретим найденную еще раз. Все вынутые вершины будут лежать на искомом цикле. | Для восстановления самого цикла достаточно при запуске поиска в глубину из очередной вершины добавлять эту вершину в стек. Когда поиск в глубину нашел вершину, которая лежит на цикле, будем последовательно вынимать вершины из стека, пока не встретим найденную еще раз. Все вынутые вершины будут лежать на искомом цикле. | ||
Строка 14: | Строка 14: | ||
== Доказательство == | == Доказательство == | ||
− | Пусть дан граф <tex>G</tex>. Запустим <tex>\mathrm{dfs}(G)</tex>. Рассмотрим выполнение процедуры поиска в глубину от некоторой вершины <tex> v </tex>. Так как все серые вершины лежат в стеке рекурсии, то для них вершина <tex> v </tex> достижима, так как между соседними вершинами в стеке есть ребро. Тогда если из рассматриваемой вершины <tex> v </tex> существует ребро в серую вершину <tex> u </tex>, то это значит, что из вершины <tex> u </tex> существует путь в <tex> v </tex> и из вершины <tex> v </tex> существует путь в <tex> u </tex> состоящий из одного ребра. И так как оба эти пути не пересекаются, то цикл существует. | + | Пусть дан граф <tex>G</tex>. Запустим <tex>\mathrm{dfs}(G)</tex>. Рассмотрим выполнение процедуры поиска в глубину от некоторой вершины <tex> v </tex>. Так как все серые вершины лежат в стеке рекурсии, то для них вершина <tex> v </tex> достижима, так как между соседними вершинами в стеке есть ребро. Тогда, если из рассматриваемой вершины <tex> v </tex> существует ребро в серую вершину <tex> u </tex>, то это значит, что из вершины <tex> u </tex> существует путь в <tex> v </tex> и из вершины <tex> v </tex> существует путь в <tex> u </tex> состоящий из одного ребра. И так как оба эти пути не пересекаются, то цикл существует. |
Докажем, что если в графе <tex>G</tex> существует цикл, то <tex>\mathrm{dfs}(G)</tex> его всегда найдет. Пусть <tex> v </tex> {{---}} первая вершина принадлежащая циклу, рассмотренная поиском в глубину. Тогда существует вершина <tex> u </tex>, принадлежащая циклу и имеющая ребро в вершину <tex> v </tex>. Так как из вершины <tex> v </tex> в вершину <tex> u </tex> существует белый путь (они лежат на одном цикле), то по [[Лемма о белых путях|лемме о белых путях]] во время выполнения процедуры поиска в глубину от вершины <tex> u </tex>, вершина <tex> v </tex> будет серой. Так как из <tex> u </tex> есть ребро в <tex> v </tex>, то это ребро в серую вершину. Следовательно <tex>\mathrm{dfs}(G)</tex> нашел цикл. | Докажем, что если в графе <tex>G</tex> существует цикл, то <tex>\mathrm{dfs}(G)</tex> его всегда найдет. Пусть <tex> v </tex> {{---}} первая вершина принадлежащая циклу, рассмотренная поиском в глубину. Тогда существует вершина <tex> u </tex>, принадлежащая циклу и имеющая ребро в вершину <tex> v </tex>. Так как из вершины <tex> v </tex> в вершину <tex> u </tex> существует белый путь (они лежат на одном цикле), то по [[Лемма о белых путях|лемме о белых путях]] во время выполнения процедуры поиска в глубину от вершины <tex> u </tex>, вершина <tex> v </tex> будет серой. Так как из <tex> u </tex> есть ребро в <tex> v </tex>, то это ребро в серую вершину. Следовательно <tex>\mathrm{dfs}(G)</tex> нашел цикл. | ||
+ | |||
+ | Заметим, что, если в графе есть вершины с петлями, то алгоритм будет работать корректно, так как при запуске поиска в глубину из такой вершины, найдется ребро, ведущее в нее же, а значит эта петля и будет являться циклом. | ||
== Реализация для случая ориентированного графа == | == Реализация для случая ориентированного графа == | ||
Строка 28: | Строка 30: | ||
print() <font color=darkgreen> // вывод ответа </font> | print() <font color=darkgreen> // вывод ответа </font> | ||
color[v] = <i>black</i> | color[v] = <i>black</i> | ||
+ | |||
+ | == См. также == | ||
+ | * [[Использование обхода в глубину для проверки связности]] | ||
+ | * [[Использование обхода в глубину для топологической сортировки]] | ||
+ | * [[Использование обхода в глубину для поиска компонент сильной связности]] | ||
+ | * [[Использование обхода в глубину для поиска точек сочленения]] | ||
+ | * [[Использование обхода в глубину для поиска мостов]] | ||
== Источники информации == | == Источники информации == |
Версия 01:26, 24 декабря 2014
Задача: |
Дан граф, требуется проверить наличие цикла в этом графе. Будем решать задачу с помощью поиска в глубину за . |
Содержание
Алгоритм
В случае ориентированного графа произведём серию поисков в глубину. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе — в чёрный. И, если поиск в глубину пытается пойти в серую вершину, то это означает, что цикл найден.
В случае неориентированного графа, используя указанный алгоритм, мы перейдем по какому-либо ребру в обратном направлении и попадем в серую вершину, что будет означать наличие цикла. Проблема в том, что по определению цикла в неориентированном графе, одно и то же ребро не может содержаться в цикле дважды. Чтобы избежать этого, необходимо передавать еще один параметр поиска в глубину — вершину, из которой мы пришли. Теперь можно считать, что цикл найден, если вершина, в которую мы хотим пойти серая и не является вершиной, из которой мы пришли.
Для восстановления самого цикла достаточно при запуске поиска в глубину из очередной вершины добавлять эту вершину в стек. Когда поиск в глубину нашел вершину, которая лежит на цикле, будем последовательно вынимать вершины из стека, пока не встретим найденную еще раз. Все вынутые вершины будут лежать на искомом цикле.
Доказательство
Пусть дан граф
. Запустим . Рассмотрим выполнение процедуры поиска в глубину от некоторой вершины . Так как все серые вершины лежат в стеке рекурсии, то для них вершина достижима, так как между соседними вершинами в стеке есть ребро. Тогда, если из рассматриваемой вершины существует ребро в серую вершину , то это значит, что из вершины существует путь в и из вершины существует путь в состоящий из одного ребра. И так как оба эти пути не пересекаются, то цикл существует.Докажем, что если в графе лемме о белых путях во время выполнения процедуры поиска в глубину от вершины , вершина будет серой. Так как из есть ребро в , то это ребро в серую вершину. Следовательно нашел цикл.
существует цикл, то его всегда найдет. Пусть — первая вершина принадлежащая циклу, рассмотренная поиском в глубину. Тогда существует вершина , принадлежащая циклу и имеющая ребро в вершину . Так как из вершины в вершину существует белый путь (они лежат на одном цикле), то поЗаметим, что, если в графе есть вершины с петлями, то алгоритм будет работать корректно, так как при запуске поиска в глубину из такой вершины, найдется ребро, ведущее в нее же, а значит эта петля и будет являться циклом.
Реализация для случая ориентированного графа
// color — массив цветов, изначально все вершины белые
func dfs(v: vertex): // v — вершина, в которой мы сейчас находимся
color[v] = grey
for (u: vu
E)
if (color[u] == white)
dfs(v)
if (color[u] == grey)
print() // вывод ответа
color[v] = black
См. также
- Использование обхода в глубину для проверки связности
- Использование обхода в глубину для топологической сортировки
- Использование обхода в глубину для поиска компонент сильной связности
- Использование обхода в глубину для поиска точек сочленения
- Использование обхода в глубину для поиска мостов
Источники информации
- MAXimal :: algo — «Проверка графа на ацикличность и нахождение цикла»
- Прикладные задачи алгоритма DFS
- Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ.[1] — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.