Использование обхода в глубину для поиска цикла — различия между версиями
(Форматирование псевдокода, замена тире, источники информации) |
(Добавлен случай неориентированности) |
||
Строка 1: | Строка 1: | ||
− | Пусть дан | + | Пусть дан без петель и кратных рёбер. Требуется проверить наличие [[Основные определения теории графов|цикла]] в этом графе. |
Решим эту задачу с помощью [[Обход в глубину, цвета вершин|поиска в глубину]] за <tex>O(M)</tex>. | Решим эту задачу с помощью [[Обход в глубину, цвета вершин|поиска в глубину]] за <tex>O(M)</tex>. | ||
Строка 5: | Строка 5: | ||
== Алгоритм == | == Алгоритм == | ||
− | Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе {{---}} в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл. | + | Пусть дан <b>ориентированный граф</b>. Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе {{---}} в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл. |
− | + | В случае <b>неориентированного графа</b> любое ребро представляется как два ребра {{---}} прямое и обратное. Тогда мы посчитаем, что эти два ребра составляют цикл, что неверно. Чтобы избежать этого, будем передавать еще один параметр поиска в глубину {{---}} вершину, из которой мы пришли. Теперь мы считаем, что нашли цикл, если вершина, в которую мы хотим пойти серая и не является вершиной из которой мы пришли. | |
+ | |||
+ | Для восстановления самого цикла достаточно при запуске поиска в глубину из очередной вершины добавлять эту вершину в стек. Когда поиск в глубину нашел вершину, которая лежит на цикле, будем последовательно вынимать вершины из стека, пока не встретим найденную еще раз. Все вынутые вершины будут лежать на искомом цикле. | ||
[[Файл: Dfs_cycle.png|thumb|200px|right| Момент нахождения цикла: синие ребра {{---}} уже пройденные, красное ребро ведет в серую, уже пройденную, вершину.]] | [[Файл: Dfs_cycle.png|thumb|200px|right| Момент нахождения цикла: синие ребра {{---}} уже пройденные, красное ребро ведет в серую, уже пройденную, вершину.]] | ||
Строка 16: | Строка 18: | ||
Докажем, что если в графе <tex>G</tex> существует цикл, то <tex>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>dfs(G)</tex> нашел цикл. | Докажем, что если в графе <tex>G</tex> существует цикл, то <tex>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>dfs(G)</tex> нашел цикл. | ||
− | == Реализация == | + | == Реализация для случая ориентированного графа == |
'''int''' graph[][]; | '''int''' graph[][]; | ||
− | '''int''' color[] <tex> \leftarrow </tex> white; | + | '''int''' color[] <tex> \leftarrow </tex> white; <font color=darkgreen> // Массив цветов, изначально все вершины белые </font> |
− | '''func''' dfs(u: '''int'''): | + | '''func''' dfs(u: '''int'''): <font color=darkgreen> // u {{---}} вершина, в которой мы сейчас находимся </font> |
− | + | color[u] = grey; | |
'''for''' (v : uv {{---}} edge) | '''for''' (v : uv {{---}} edge) | ||
if (color[v] == white) | if (color[v] == white) | ||
Строка 28: | Строка 30: | ||
if (color[v] == grey) | if (color[v] == grey) | ||
print(); <font color=darkgreen> // вывод ответа </font> | print(); <font color=darkgreen> // вывод ответа </font> | ||
− | color[u] = black; | + | color[u] = black; |
+ | |||
+ | == Реализация для случая неориентированного графа == | ||
+ | |||
+ | '''int''' graph[][]; | ||
+ | '''int''' color[] <tex> \leftarrow </tex> white; <font color=darkgreen> // Массив цветов, изначально все вершины белые </font> | ||
+ | '''func''' dfs(u, par: '''int'''): <font color=darkgreen> // u {{---}} вершина, в которой мы сейчас находимся; par {{---}} вершина, из которой мы пришли в u </font> | ||
+ | color[u] = grey; | ||
+ | '''for''' (v : uv {{---}} edge) | ||
+ | if (color[v] == white) | ||
+ | dfs(v, u); | ||
+ | if (color[v] == grey '''and''' v <tex>\neq</tex> par) | ||
+ | print(); <font color=darkgreen> // вывод ответа </font> | ||
+ | color[u] = black; | ||
== Источники информации == | == Источники информации == | ||
* [http://e-maxx.ru/algo/finding_cycle MAXimal :: algo {{---}} «Проверка графа на ацикличность и нахождение цикла»] | * [http://e-maxx.ru/algo/finding_cycle MAXimal :: algo {{---}} «Проверка графа на ацикличность и нахождение цикла»] |
Версия 12:14, 23 декабря 2014
Пусть дан без петель и кратных рёбер. Требуется проверить наличие цикла в этом графе.
Решим эту задачу с помощью поиска в глубину за .
Содержание
Алгоритм
Пусть дан ориентированный граф. Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе — в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл.
В случае неориентированного графа любое ребро представляется как два ребра — прямое и обратное. Тогда мы посчитаем, что эти два ребра составляют цикл, что неверно. Чтобы избежать этого, будем передавать еще один параметр поиска в глубину — вершину, из которой мы пришли. Теперь мы считаем, что нашли цикл, если вершина, в которую мы хотим пойти серая и не является вершиной из которой мы пришли.
Для восстановления самого цикла достаточно при запуске поиска в глубину из очередной вершины добавлять эту вершину в стек. Когда поиск в глубину нашел вершину, которая лежит на цикле, будем последовательно вынимать вершины из стека, пока не встретим найденную еще раз. Все вынутые вершины будут лежать на искомом цикле.
Доказательство
Пусть дан граф
. Запустим . Рассмотрим выполнение процедуры поиска в глубину от некоторой вершины . Так как все серые вершины лежат в стеке рекурсии, то для них вершина достижима, так как между соседними вершинами в стеке есть ребро. Тогда если из рассматриваемой вершины существует ребро в серую вершину , то это значит, что из вершины существует путь в и из вершины существует путь в состоящий из одного ребра. И так как оба эти пути не пересекаются, то цикл существует.Докажем, что если в графе лемме о белых путях во время выполнения процедуры поиска в глубину от вершины , вершина будет серой. Так как из есть ребро в , то это ребро в серую вершину. Следовательно нашел цикл.
существует цикл, то его всегда найдет. Пусть — первая вершина принадлежащая циклу, рассмотренная поиском в глубину. Тогда существует вершина , принадлежащая циклу и имеющая ребро в вершину . Так как из вершины в вершину существует белый путь (они лежат на одном цикле), то поРеализация для случая ориентированного графа
int graph[][];
int color[]
white; // Массив цветов, изначально все вершины белые
func dfs(u: int): // u — вершина, в которой мы сейчас находимся color[u] = grey; for (v : uv — edge) if (color[v] == white) dfs(v); if (color[v] == grey) print(); // вывод ответа color[u] = black;
Реализация для случая неориентированного графа
int graph[][];
int color[]
white; // Массив цветов, изначально все вершины белые
func dfs(u, par: int): // u — вершина, в которой мы сейчас находимся; par — вершина, из которой мы пришли в u
color[u] = grey;
for (v : uv — edge)
if (color[v] == white)
dfs(v, u);
if (color[v] == grey and v
par)
print(); // вывод ответа
color[u] = black;
Источники информации
- MAXimal :: algo — «Проверка графа на ацикличность и нахождение цикла»
- Прикладные задачи алгоритма DFS
- Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ.[1] — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.