Обход в глубину, цвета вершин — различия между версиями
Glukos (обсуждение | вклад) |
Glukos (обсуждение | вклад) (→Пошаговое представление) |
||
Строка 7: | Строка 7: | ||
=== Пошаговое представление === | === Пошаговое представление === | ||
− | #Выбираем любую вершину из еще ''не пройденных'', обозначим ее как u. | + | #Выбираем любую вершину из еще ''не пройденных'', обозначим ее как <tex>u</tex>. |
#Запускаем процедуру <tex>dfs(u)</tex> | #Запускаем процедуру <tex>dfs(u)</tex> | ||
#*Помечаем вершину u как ''пройденную'' | #*Помечаем вершину u как ''пройденную'' | ||
#*Для каждой ''не пройденной'' смежной с <tex>u</tex> вершиной (назовем ее <tex>v</tex>) запускаем <tex>dfs(v)</tex> | #*Для каждой ''не пройденной'' смежной с <tex>u</tex> вершиной (назовем ее <tex>v</tex>) запускаем <tex>dfs(v)</tex> | ||
− | #Повторяем шаги 1 и 2, пока все вершины не окажутся ''пройденными''. | + | #Повторяем шаги 1 и 2, пока все вершины не окажутся ''пройденными''. |
=== Реализация === | === Реализация === |
Версия 02:44, 25 октября 2011
Обход в глубину (поиск в глубину, англ. Depth-First Search, DFS) - один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки.
Содержание
Алгоритм
Общая идея
Общая идея алгоритма состоит в следующем: для каждой не пройденной вершины необходимо найти все не пройденные смежные вершины и повторить поиск для них.
Пошаговое представление
- Выбираем любую вершину из еще не пройденных, обозначим ее как .
- Запускаем процедуру
- Помечаем вершину u как пройденную
- Для каждой не пройденной смежной с вершиной (назовем ее ) запускаем
- Повторяем шаги 1 и 2, пока все вершины не окажутся пройденными.
Реализация
vector<bool> visited; //вектор для хранения информации о пройденных и не пройденных вершинах void dfs(int u) { visited[u] = true; //помечаем вершину как пройденную for (v таких, что (u, v) - ребро в G) //проходим по смежным с u вершинам if (!visited[v]) //проверяем, не находились ли мы ранее в выбранной вершине dfs(v); } int main() { ... //задание графа G с количеством вершин n. visited.assign(n, false); //в начале все вершины в графе не пройденные for (int i = 0; i < n; ++i) //проходим по всем вершинам графа... if (!visited[i]) //...не забыв проверить, были мы уже в очередной вершине или нет dfs(i); return 0; }
Время работы
Оценим время работы обхода в глубину. Процедура
вызывается от каждой вершины не более одного раза, а внутри процедуры рассматриваются все такие ребра . Всего таких ребер для всех вершин в графе , следовательно, время работы алгоритма оценивается как .Цвета вершин
Зачастую, простой информации "были/не были в вершине" не хватает для конкретных целей.
Поэтому в процессе алгоритма вершинам задают некоторые цвета:
- если вершина белая, значит, мы в ней еще не были, вершина не пройдена;
- серая - вершина проходится в текущей процедуре
- черная - вершина пройдена, все итерации
Такие "метки" в основном используются при поиске цикла.
Реализация
Отличие реализации с цветами от предыдущей лишь в массиве visited, который мы назовем теперь color, и дополнительной черной "метки". При этом цвета вершин будут заданы следующим образом: белый - 0, серый - 1, черный - 2.
vector<int> color; //вектор для хранения информации о цвете вершин void dfs(int u) { color[u] = 1; //раскрашиваем вершину в серый цвет for (v таких, что (u, v) - ребро в G) //проходим по смежным с u вершинам if (!color[v]) //проверяем, не находились ли мы ранее в выбранной вершине, условие не требует изменений, dfs(v); //поскольку мы считаем вершину "не пройденной" только тогда, когда она белого цвета, т.е. когда color[v] = 0 color[u] = 2; //раскрашиваем вершину в черный цвет } int main() { ... //задание графа G с количеством вершин n. color.assign(n, 0); //в начале все вершины в графе не пройденные, т.е. белые. for (int i = 0; i < n; ++i) //проходим по всем вершинам графа... if (!color[i]) //...не забыв проверить, были мы уже в очередной вершине или нет dfs(i); return 0; }
Дерево обхода в глубину
Рассмотрим подграф предшествования обхода в глубину
, где , где в свою очередь — вершина, от которой был вызван (для вершин, от которых был вызван нерекурсивно это значение соответственно равно ). Подграф предшествования поиска в глубину образует лес обхода в глубину, который состоит из нескольких деревьев обхода в глубину. Назовем ребрами дерева те ребра из , которые вошли в . Рассмотрим такие ребра графа , которые не попали в . Ребра , соединяющие вершину с её предком в дереве обхода в глубину назовем обратными ребрами. Ребра , не являющиеся ребрами дерева и соединяющие вершину с её потомком в дереве обхода в глубину назовем прямыми ребрами. Все остальные ребра назовем перекрестными ребрами — такие ребра могут соединять вершины одного и того же дерева обхода в глубину, когда ни одна из вершин не является предком другой, или соединять вершины в разных деревьях. Алгоритм можно модифицировать так, что он будет классифицировать встречающиеся при работе ребра. Ключевая идея состоит в том, что каждое ребро можно классифицировать при помощи цвета вершины при первом его исследовании, а именно:- Белый цвет вершины по определению говорит о том, что это ребро дерева.
- Серый цвет в силу того, что серые вершины всегда образуют нисходящий путь в каком-либо из деревьев и встреченная вершина лежит на нем выше вершины , определяет обратное ребро.
- Черный цвет, соответственно, указывает на прямое или перекрестное ребро.