Обход в глубину, цвета вершин — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Дерево обхода в глубину)
Строка 1: Строка 1:
'''Обход в глубину''' (поиск в глубину, англ. '''Depth-First Search''', '''DFS''') — один из основных методов обхода [[Основные определения теории графов|графа]], часто используемый для [[Использование обхода в глубину для проверки связности|проверки связности]], поиска [[Использование обхода в глубину для поиска цикла в ориентированном графе|цикла]] и [[Использование обхода в глубину для поиска компонент сильной связности|компонент сильной связности]] и для [[Использование обхода в глубину для топологической сортировки|топологической сортировки]].
+
'''Обход в глубину''' (поиск в глубину, англ. ''Depth-First Search'', ''DFS'') — один из основных методов обхода [[Основные определения теории графов|графа]], часто используемый для [[Использование обхода в глубину для проверки связности|проверки связности]], поиска [[Использование обхода в глубину для поиска цикла в ориентированном графе|цикла]] и [[Использование обхода в глубину для поиска компонент сильной связности|компонент сильной связности]] и для [[Использование обхода в глубину для топологической сортировки|топологической сортировки]].
  
 
== Алгоритм ==
 
== Алгоритм ==
Строка 14: Строка 14:
  
 
=== Реализация ===
 
=== Реализация ===
vector<bool> visited;                      //вектор для хранения информации о ''пройденных'' и ''не пройденных'' вершинах
+
В массиве <tex>visited[]</tex> хранится информация о ''пройденных'' и ''не пройденных'' вершинах.
+
 
  void dfs(int u)               
+
  '''function''' dfs(u: '''int'''):              
{
+
    visited[u] = true                
    visited[u] = true;                      //помечаем вершину как пройденную
+
    '''for''' v: (u, v) '''in''' G
    for (v таких, что (u, v) — ребро в G)  //проходим по смежным с u вершинам
+
      '''if''' '''not''' visited[v]
        if (!visited[v])                    //проверяем, не находились ли мы ранее в выбранной вершине
+
          dfs(v)
            dfs(v);
+
 
}
+
Вызов обхода в глубину из основной программы осуществляется так:
+
  '''function''' main(): '''int'''
  int main()
+
    <font color=darkgreen>//задание графа G с количеством вершин n </font>
{
+
    '''fill'''(visited, false)            
    ...                                    //задание графа G с количеством вершин n.
+
    '''for''' i = 1 '''to''' n             
    visited.assign(n, false);              //в начале все вершины в графе ''не пройденные''
+
        '''if''' '''not''' visited[i]                     
    for (int i = 0; i < n; ++i)             //проходим по всем вершинам графа...
+
          dfs(i)
        if (!visited[i])                   //...не забыв проверить, были мы уже в очередной вершине или нет
 
            dfs(i);
 
    return 0;
 
}
 
  
 
=== Время работы ===
 
=== Время работы ===
Строка 38: Строка 34:
  
 
== Цвета вершин ==
 
== Цвета вершин ==
Зачастую, простой информации "были/не были в вершине" не хватает для конкретных целей.<br />
+
Зачастую, простой информации "были/не были в вершине" не хватает для конкретных целей.
Поэтому в процессе алгоритма вершинам задают некоторые цвета:<br />
+
 
:если вершина ''белая'', значит, мы в ней еще не были, вершина ''не пройдена'';<br />
+
Поэтому в процессе алгоритма вершинам задают некоторые цвета:
:''серая'' — вершина ''проходится'' в текущей процедуре <tex>dfs</tex>;<br />
+
 
:''черная'' — вершина ''пройдена'', все итерации <tex>dfs</tex> от нее завершены.<br />
+
*если вершина ''белая'', значит, мы в ней еще не были, вершина ''не пройдена'';
 +
*''серая'' — вершина ''проходится'' в текущей процедуре <tex>dfs</tex>;
 +
*''черная'' — вершина ''пройдена'', все итерации <tex>dfs</tex> от нее завершены.
  
 
Такие "метки" в основном используются при [[Использование обхода в глубину для поиска цикла в ориентированном графе|поиске цикла]].
 
Такие "метки" в основном используются при [[Использование обхода в глубину для поиска цикла в ориентированном графе|поиске цикла]].
  
 
=== Реализация ===
 
=== Реализация ===
Отличие реализации с цветами от предыдущей лишь в массиве visited, который мы назовем теперь color.
+
Отличие реализации с цветами от предыдущей лишь в массиве <tex>visited[]</tex>, который мы назовем теперь <tex>color[]</tex>. В нем будет хранится информация о цветах вершин.
 +
 
 +
'''function''' dfs(u: '''int'''):             
 +
    color[u] = ''gray''                     
 +
    '''for''' v: (u, v) '''in''' G
 +
      '''if''' color[v] == ''white''
 +
          dfs(v)
 +
    color[u] = ''black''
 +
 +
Вызов обхода в глубину из основной программы осуществляется так:
 +
'''function''' main(): '''int'''
 +
    <font color=darkgreen>//задание графа G с количеством вершин n </font>
 +
    '''fill'''(color, ''white'')           
 +
    '''for''' i = 1 '''to''' n           
 +
        '''if''' color[i] == ''white''               
 +
          dfs(i)
 +
 
 +
=== Пример ===
 +
Рассмотрим, как будут изменяться цвета вершин при обходе в глубину данного графа.
  
vector<color_t> color;                         //вектор для хранения информации о цвете вершин
+
{| style="background-color:#CCC;margin:0.5px;width:600px"
+
!style="background-color:#EEE"| Описание шага
void dfs(int u)             
+
!style="background-color:#EEE"| Состояние графа
{
+
|-
    color[u] = GRAY;                           //раскрашиваем вершину в ''серый'' цвет
+
|style="background-color:#FFF;padding:2px 10px"| Из основной программы проверяем, что первая вершина окрашена в белый цвет. Заходим в нее и раскрашиваем ее в серый цвет.
    for (v таких, что (u, v) — ребро в G)  //проходим по смежным с u вершинам
+
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs1.png‎|150px|]]
        if (color[v] == WHITE)                      //проверяем, не находились ли мы ранее в выбранной вершине, условие не требует изменений,  
+
|-
            dfs(v);                         //поскольку мы считаем вершину "не пройденной" только тогда, когда она ''белого'' цвета, т.е. когда color[v] == WHITE
+
|style="background-color:#FFF;padding:2px 10px"| Пробуем пойти в вершину с номером 2. Проверяем, что она белая, и переходим в нее. Окрашиваем ее в серый цвет.
    color[u] = BLACK;                           //раскрашиваем вершину в ''черный'' цвет
+
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs2.png‎|150px|]]
}
+
|-
+
|style="background-color:#FFF;padding:2px 10px"| Пробуем пойти в вершину с номером 3. Проверяем, что она белая, и переходим в нее. Окрашиваем ее в серый цвет.
int main()
+
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs3.png‎|150px|]]
{
+
|-
    ...                                    //задание графа G с количеством вершин n.
+
|style="background-color:#FFF;padding:2px 10px"| Проверяем, что из вершины с номером 3 не исходит ни одного ребра. Помечаем ее в черный цвет и возвращаемся в вершину с номером 2.
    color.assign(n, WHITE);                   //в начале все вершины в графе ''не пройденные'', т.е. ''белые''.
+
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs4.png‎|150px|]]
    for (int i = 0; i < n; ++i)            //проходим по всем вершинам графа...
+
|-
        if (color[i] == WHITE)                    //...не забыв проверить, были мы уже в очередной вершине или нет
+
|style="background-color:#FFF;padding:2px 10px"| Пробуем пойти в вершину с номером 4. Проверяем, что она белая, и переходим в нее. Окрашиваем ее в серый цвет.
            dfs(i);
+
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs5_6_7.png‎|150px|]]
    return 0;
+
|-
}
+
|style="background-color:#FFF;padding:2px 10px"| Пробуем пойти в вершину с номером 3. Видим, что она черного цвета, и остаемся на месте.
 +
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs5_6_7.png‎|150px|]]
 +
|-
 +
|style="background-color:#FFF;padding:2px 10px"| Пробуем пойти в вершину с номером 1. Видим, что она серого цвета, и остаемся на месте.
 +
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs5_6_7.png‎|150px|]]
 +
|-
 +
|style="background-color:#FFF;padding:2px 10px"| Из вершины с номером 4 больше нет исходящих ребер. Помечаем ее в черный цвет и возвращаемся в вершину с номером 2.
 +
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs8.png‎|150px|]]
 +
|-
 +
|style="background-color:#FFF;padding:2px 10px"| Из вершины с номером 2 больше нет исходящих ребер. Помечаем ее в черный цвет и возвращаемся в вершину с номером 1.
 +
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs9.png‎|150px|]]
 +
|-
 +
|style="background-color:#FFF;padding:2px 10px"| Из вершины с номером 1 больше нет исходящих ребер. Помечаем ее в черный цвет и выходим в основную программу. Из основной программы проверяем, что все вершины окрашены в черный цвет. Алгоритм завершен.
 +
|style="background-color:#FFF;padding:2px 10px"| [[Файл:dfs10.png‎|150px|]]
 +
|}
  
 
== Дерево обхода в глубину ==
 
== Дерево обхода в глубину ==
Строка 87: Строка 117:
 
* Черный цвет, соответственно, указывает на ''прямое'' или ''перекрестное ребро''.
 
* Черный цвет, соответственно, указывает на ''прямое'' или ''перекрестное ребро''.
  
== Источники ==
+
== Источники информации ==
*[http://ru.wikipedia.org/wiki/Поиск_в_глубину Обход в глубину на ru.wikipedia.org]
+
*[http://ru.wikipedia.org/wiki/Поиск_в_глубину Википедия {{---}} Поиск в глубину]
*[http://en.wikipedia.org/wiki/Depth-first_search Обход в глубину на en.wikipedia.org]
+
*[http://en.wikipedia.org/wiki/Depth-first_search Wikipedia {{---}} Depth-first search]
 
*[http://www.e-maxx.ru/algo/dfs Обход в глубину. Реализации.]
 
*[http://www.e-maxx.ru/algo/dfs Обход в глубину. Реализации.]
 
* Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. Алгоритмы: построение и анализ, второе издание. Пер. с англ. — Издательский дом "Вильямс", 2007. — 1296 с. — Глава 22. Элементарные алгоритмы для работы с графами.
 
* Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. Алгоритмы: построение и анализ, второе издание. Пер. с англ. — Издательский дом "Вильямс", 2007. — 1296 с. — Глава 22. Элементарные алгоритмы для работы с графами.

Версия 22:53, 11 декабря 2016

Обход в глубину (поиск в глубину, англ. Depth-First Search, DFS) — один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки.

Алгоритм

Общая идея

Общая идея алгоритма состоит в следующем: для каждой не пройденной вершины необходимо найти все не пройденные смежные вершины и повторить поиск для них.

Пошаговое представление

  1. Выбираем любую вершину из еще не пройденных, обозначим ее как [math]u[/math].
  2. Запускаем процедуру [math]dfs(u)[/math]
    • Помечаем вершину [math]u[/math] как пройденную
    • Для каждой не пройденной смежной с [math]u[/math] вершиной (назовем ее [math]v[/math]) запускаем [math]dfs(v)[/math]
  3. Повторяем шаги 1 и 2, пока все вершины не окажутся пройденными.

Реализация

В массиве [math]visited[][/math] хранится информация о пройденных и не пройденных вершинах.

function dfs(u: int):              
   visited[u] = true                  
   for v: (u, v) in G
      if not visited[v]
         dfs(v)

Вызов обхода в глубину из основной программы осуществляется так:

function main(): int
   //задание графа G с количеством вершин n 
   fill(visited, false)             
   for i = 1 to n             
       if not visited[i]                    
          dfs(i)

Время работы

Оценим время работы обхода в глубину. Процедура [math]dfs[/math] вызывается от каждой вершины не более одного раза, а внутри процедуры рассматриваются все такие ребра [math]\{e\ |\ begin(e) = u\}[/math]. Всего таких ребер для всех вершин в графе [math]O(E)[/math], следовательно, время работы алгоритма оценивается как [math]O(V+E)[/math].

Цвета вершин

Зачастую, простой информации "были/не были в вершине" не хватает для конкретных целей.

Поэтому в процессе алгоритма вершинам задают некоторые цвета:

  • если вершина белая, значит, мы в ней еще не были, вершина не пройдена;
  • серая — вершина проходится в текущей процедуре [math]dfs[/math];
  • черная — вершина пройдена, все итерации [math]dfs[/math] от нее завершены.

Такие "метки" в основном используются при поиске цикла.

Реализация

Отличие реализации с цветами от предыдущей лишь в массиве [math]visited[][/math], который мы назовем теперь [math]color[][/math]. В нем будет хранится информация о цветах вершин.

function dfs(u: int):              
   color[u] = gray                      
   for v: (u, v) in G
      if color[v] == white
         dfs(v)
   color[u] = black

Вызов обхода в глубину из основной программы осуществляется так:

function main(): int
   //задание графа G с количеством вершин n 
   fill(color, white)             
   for i = 1 to n             
       if color[i] == white                
          dfs(i)

Пример

Рассмотрим, как будут изменяться цвета вершин при обходе в глубину данного графа.

Описание шага Состояние графа
Из основной программы проверяем, что первая вершина окрашена в белый цвет. Заходим в нее и раскрашиваем ее в серый цвет. Dfs1.png
Пробуем пойти в вершину с номером 2. Проверяем, что она белая, и переходим в нее. Окрашиваем ее в серый цвет. Dfs2.png
Пробуем пойти в вершину с номером 3. Проверяем, что она белая, и переходим в нее. Окрашиваем ее в серый цвет. Dfs3.png
Проверяем, что из вершины с номером 3 не исходит ни одного ребра. Помечаем ее в черный цвет и возвращаемся в вершину с номером 2. Dfs4.png
Пробуем пойти в вершину с номером 4. Проверяем, что она белая, и переходим в нее. Окрашиваем ее в серый цвет. Dfs5 6 7.png
Пробуем пойти в вершину с номером 3. Видим, что она черного цвета, и остаемся на месте. Dfs5 6 7.png
Пробуем пойти в вершину с номером 1. Видим, что она серого цвета, и остаемся на месте. Dfs5 6 7.png
Из вершины с номером 4 больше нет исходящих ребер. Помечаем ее в черный цвет и возвращаемся в вершину с номером 2. Dfs8.png
Из вершины с номером 2 больше нет исходящих ребер. Помечаем ее в черный цвет и возвращаемся в вершину с номером 1. Dfs9.png
Из вершины с номером 1 больше нет исходящих ребер. Помечаем ее в черный цвет и выходим в основную программу. Из основной программы проверяем, что все вершины окрашены в черный цвет. Алгоритм завершен. Dfs10.png

Дерево обхода в глубину

Типы ребер, определяемые деревом обхода:
1) ребра дерева
2) обратные ребра
3) прямые ребра
4) перекрестные ребра

Рассмотрим подграф предшествования обхода в глубину [math]G_p = (V, E_p)[/math], где [math]E_p = \{(p[u], u) : u \in V,\ p[u] \neq NIL\}[/math], где в свою очередь [math]p[u][/math] — вершина, от которой был вызван [math]dfs(u)\ [/math] (для вершин, от которых [math]dfs[/math] был вызван нерекурсивно это значение соответственно равно [math]NIL[/math]). Подграф предшествования поиска в глубину образует лес обхода в глубину, который состоит из нескольких деревьев обхода в глубину. С помощью полученного леса можно классифицировать ребра графа [math]G[/math]:

  • Ребрами дерева назовем те ребра из [math]G[/math], которые вошли в [math]G_p[/math].
  • Ребра [math](u, v)[/math], соединяющие вершину [math]u[/math] с её предком [math]v[/math] в дереве обхода в глубину назовем обратными ребрами (для неориентированного графа предок должен быть не родителем, так как иначе ребро будет являться ребром дерева).
  • Ребра [math](u, v)[/math], не являющиеся ребрами дерева и соединяющие вершину [math]u[/math] с её потомком [math]v[/math] в дереве обхода в глубину назовем прямыми ребрами (в неориентированном графе нет разницы между прямыми и обратными ребрами, поэтому все такие ребра считаются обратными).
  • Все остальные ребра назовем перекрестными ребрами — такие ребра могут соединять вершины одного и того же дерева обхода в глубину, когда ни одна из вершин не является предком другой, или соединять вершины в разных деревьях.

Алгоритм [math]dfs[/math] можно модифицировать так, что он будет классифицировать встречающиеся при работе ребра. Ключевая идея состоит в том, что каждое ребро [math](u, v)[/math] можно классифицировать при помощи цвета вершины [math]v[/math] при первом его исследовании, а именно:

  • Белый цвет вершины [math]v[/math] по определению [math]dfs[/math] говорит о том, что это ребро дерева.
  • Серый цвет в силу того, что серые вершины всегда образуют нисходящий путь в каком-либо из деревьев [math]dfs[/math] и встреченная вершина [math]v[/math] лежит на нем выше вершины [math]u[/math], определяет обратное ребро (для неориентированного графа необходимо проверить условие [math]v \neq p[u][/math]).
  • Черный цвет, соответственно, указывает на прямое или перекрестное ребро.

Источники информации