Использование обхода в глубину для поиска компонент сильной связности — различия между версиями
Dimitrova (обсуждение | вклад) |
Dimitrova (обсуждение | вклад) (→Пример реализации) |
||
Строка 36: | Строка 36: | ||
В итоге получаем, что время работы алгоритма <tex>O(V + E)</tex>. | В итоге получаем, что время работы алгоритма <tex>O(V + E)</tex>. | ||
− | == | + | ==Псевдокод== |
− | + | <tex>G, H</tex> //g хранит граф в виде списка смежностей, h - инвертированный | |
− | + | <tex>ord, component</tex> //список вершин в порядке окончания обработки, номер компоненты, к который относиться вершина | |
− | |||
− | + | '''dfs'''(<tex>v</tex>) //первый поиск в глубину, определяющий порядок обхода | |
− | + | <tex>color[v] \leftarrow 1</tex> | |
− | color[v] | + | '''for''' (всех <tex>i</tex> смежных с <tex>v</tex>) |
− | for ( | + | '''if''' (вершина <tex>i</tex> не посещена) |
− | + | '''dfs'''(<tex>G[v][i]</tex>) | |
− | if ( | + | Добавляем вершину <tex>v</tex> в конец списка <tex>ord</tex> |
− | dfs( | ||
− | |||
− | |||
− | |||
− | + | '''dfs2'''(<tex>v</tex>) //второй поиск в глубину, выявляет компоненты сильной связности в графе | |
− | + | <tex>component[v] \leftarrow col</tex> | |
− | component[v] | + | '''for''' (всех <tex>i</tex> смежных с <tex>v</tex>) |
− | for ( | + | '''if''' (если вершина <tex>i</tex> еще не находится ни в какой компоненте) |
− | + | '''dfs2'''(<tex>H[v][i]</tex>); | |
− | if ( | ||
− | dfs2( | ||
− | |||
− | |||
− | + | '''main'''() | |
− | + | считываем исходные данные, формируем массивы <tex>G</tex> и <tex>H</tex> | |
− | + | '''for''' (по всем вершинам <tex>i</tex> графа <tex>G</tex>) //формируем массив ord[] | |
− | for ( | + | '''if''' ( вершина <tex>i</tex> не посещена) |
− | + | '''dfs'''(i); | |
− | if ( | + | <tex>col \leftarrow 1;</tex> |
− | dfs(i); | + | '''for''' (по всем вершинам <tex>i</tex> списка <tex>ord[]</tex> в обратном порядке) //от сохранённого в ord[], что соответствует уменьшению времени конца обработки f[] |
− | + | '''if''' (если вершина <tex>i</tex> не находится ни в какой компоненте) | |
− | + | '''dfs2'''(<tex>i</tex>), <tex>col</tex>++; | |
− | for ( | ||
− | |||
− | if ( | ||
− | dfs2( | ||
− | |||
− | |||
По окончании выполнения алгоритма в <tex>component[i]</tex> имеем номер компоненты, к которой принадлежит вершина <tex>i</tex>. | По окончании выполнения алгоритма в <tex>component[i]</tex> имеем номер компоненты, к которой принадлежит вершина <tex>i</tex>. |
Версия 21:25, 23 ноября 2011
Содержание
Алгоритм
Компоненты сильной связанности можно найти с помощью поиска в глубину в 3 этапа:
- Построить граф с обратными (инвертированными) рёбрами
- Выполнить в поиск в глубину и найти - время окончания обработки вершины
- Выполнить поиск в глубину в , перебирая вершины во внешнем цикле в порядке убывания
Полученные на 3-ем этапе деревья поиска в глубину будут являться компонентами сильной связности графа
Так как компоненты сильной связности и графа совпадают, то первый поиск в глубину для нахождения можно выполнить на графе , а второй - на .
Доказательство корректности алгоритма
Теорема: |
Вершины компонентe сильной связанности. и взаимно достижимы после выполнения алгоритма они оказываются в одной |
Доказательство: |
Если вершины и были взаимно достижимы в графе , то во время выполнения третьего шага алгоритма обе вершины окажутся в одном поддереве по свойству обхода в глубина. Следовательно они будут находится в одной компоненте сильной связности.
1) Рассмотрим корень дерева второго обхода в глубину, в котором оказались вершины и . Это значит, что в графе существует путь из в и в .2) Вершина была рассмотрена вторым обходом в глубину раньше, чем и , значит время выхода из нее при первом обходе в глубину больше, чем время выхода из вершин и . Из этого мы получаем 2 случая:а) Обе эти вершины были достижимы из в инвертированном графе. А это означает взаимную достижимость вершин и и взаимную достижимость вершин и . А складывая пути мы получаем взаимную достижимость вершин и .б) Между Значит, из случая а) и не существования случая б) получаем, что вершины и этими вершинами вообще нет пути ни в одну сторону, ни в другую при первом обходе в инверитированном графе. Но последнего быть не может, так как эти вершины были достижимы из в графе , а значит, вершина достижима из них в графе . и взаимно достижимы в обоих графах. |
Время работы алгоритма
- Для того, чтобы инвертировать все ребра в графе, представленном в виде списка потребуется действий. Для матричного представления графа ненужно выполнять никакие действия для его инвертирования.
- Количество ребер в инвертированном равно количеству ребер в изначальном графе, поэтому поиск в глубину будет работать за
- Поиск в глубину в исходном графе выполняется за .
В итоге получаем, что время работы алгоритма
.Псевдокод
//g хранит граф в виде списка смежностей, h - инвертированный //список вершин в порядке окончания обработки, номер компоненты, к который относиться вершина dfs( ) //первый поиск в глубину, определяющий порядок обхода for (всех смежных с ) if (вершина не посещена) dfs( ) Добавляем вершину в конец списка dfs2( ) //второй поиск в глубину, выявляет компоненты сильной связности в графе for (всех смежных с ) if (если вершина еще не находится ни в какой компоненте) dfs2( ); main() считываем исходные данные, формируем массивы и for (по всем вершинам графа ) //формируем массив ord[] if ( вершина не посещена) dfs(i); for (по всем вершинам списка в обратном порядке) //от сохранённого в ord[], что соответствует уменьшению времени конца обработки f[] if (если вершина не находится ни в какой компоненте) dfs2( ), ++;
По окончании выполнения алгоритма в
имеем номер компоненты, к которой принадлежит вершина .Литература
- Р.Седжвик. "Фундаментальные алгоритмы на С++. Алгоритмы на графах" - СПб, ДиаСофтЮП, 2002