Алгоритм Тарьяна: различия между версиями
[непроверенная версия] | [непроверенная версия] |
Нет описания правки |
|||
Строка 4: | Строка 4: | ||
Этот алгоритм основан на том, что: |
Этот алгоритм основан на том, что: |
||
# Вершины рассматриваются в обратном топологическом порядке, поэтому в конце рекурсивной функции для исходной вершины не будет встречено ни одной вершины из той же сильной |
# Вершины рассматриваются в обратном топологическом порядке, поэтому в конце рекурсивной функции для исходной вершины не будет встречено ни одной вершины из той же компоненты сильной связности, так как все вершины, достижимые из исходной, уже обработаны. |
||
# Обратные связи в дереве дают второй путь из одной вершины в другую и связывают |
# Обратные связи в дереве дают второй путь из одной вершины в другую и связывают компоненты сильной связности в одну. |
||
== Неформальное описание алгоритма == |
== Неформальное описание алгоритма == |
||
Строка 12: | Строка 12: | ||
Алгоритм Тарьяна можно понимать как вариацию алгоритма [[поиск в глубину|поиска в глубину]], в котором при посещении вершины и окончании обработки вершины выполняются дополнительные действия. Посещение вершины происходит при движении от корня к листьям, а окончание обработки вершины — на обратном пути. При посещении вершины она проталкивается во вспомогательный стек, при окончании обработки компоненты сильной связности все её вершины выталкиваются из этого стека{{sfn|Джереми Сик и др.|2006}}. |
Алгоритм Тарьяна можно понимать как вариацию алгоритма [[поиск в глубину|поиска в глубину]], в котором при посещении вершины и окончании обработки вершины выполняются дополнительные действия. Посещение вершины происходит при движении от корня к листьям, а окончание обработки вершины — на обратном пути. При посещении вершины она проталкивается во вспомогательный стек, при окончании обработки компоненты сильной связности все её вершины выталкиваются из этого стека{{sfn|Джереми Сик и др.|2006}}. |
||
== Алгоритм в псевдокоде == |
|||
Индексы компонент связности всех вершин хранятся в векторе id, индексированном номерами вершин. Вектор low отслеживает вершину с наименьшим номером в прямом порядке обхода, достижимую из каждого узла через последовательность прямых связей, за которыми следует одна восходящая связь. Воспользовавшись поиском в глубину с тем, чтобы рассматривать вершины в обратном топологическом порядке, мы вычисляем для каждой вершины v максимальную точку, достижимую через обратную связь из предшественника (low[v]). Когда для вершины v выполняется pre[v]=low[v], мы выталкиваем её из стека, а также все вершины выше её и всем им присваиваем номер следующей компоненты{{Нет АИ|7|10|2012}}.<!-- мутно. переписать в терминах рисунка --> |
|||
''// входные данные: ориентированный граф G = (V, A) '' |
|||
''// выходные данные: множество компонент сильной связности '' |
|||
''index'' := 0 |
|||
''S'' := empty stack |
|||
'''for each''' ''v'' '''in''' ''V'' '''do''' |
|||
'''if''' ''v''.index is undefined '''then''' |
|||
strongconnect(''v'') |
|||
'''function''' strongconnect(''v'') |
|||
''// В index храним количество ранее обработанных вершин, v.index - это "время входа" в вершину v'' |
|||
''v''.index := ''index'' |
|||
''v''.lowlink := ''index'' |
|||
''index'' := ''index'' + 1 |
|||
''S''.push(''v'') |
|||
''// Поле onStack нужно, чтобы проверять принадлежность вершины стеку за O(1)'' |
|||
''v''.onStack := true |
|||
''// Перебираем дуги, исходящие из v'' |
|||
'''for each''' (''v'', ''w'') '''in''' ''A'' '''do''' |
|||
'''if''' ''w''.index is undefined '''then''' |
|||
''// Вершина w ранее не посещалась; запустимся из неё рекурсивно'' |
|||
strongconnect(''w'') |
|||
''v''.lowlink := min(''v''.lowlink, ''w''.lowlink) |
|||
'''else if''' ''w''.onStack '''then''' |
|||
''// Вершина w находится в стеке S, значит, принадлежит той же компоненте сильной связности, что и v'' |
|||
''// Если w не в стеке, значит, дуга (''v'', ''w'') ведёт в ранее обработанную компоненту сильной связности и должна быть проигнорирована |
|||
''// Замечание: в следующей строке намеренно используется w.index вместо w.lowlink - это отсылает к исходной статье Тарьяна'' |
|||
''// Если заменить w.index на w.lowlink, алгоритм останется корректным'' |
|||
''v''.lowlink := min(''v''.lowlink, ''w''.index) |
|||
''// Вершина v - корень текущей компоненты сильной связности, все верхние вершины стека образуют эту компоненту'' |
|||
'''if''' ''v''.lowlink = ''v''.index '''then''' |
|||
создать новую компоненту сильной связности |
|||
'''repeat''' |
|||
''w'' := ''S''.pop() |
|||
''w''.onStack := false |
|||
добавить ''w'' в текущую компоненту сильной связности |
|||
'''while''' ''w'' ≠ ''v'' |
|||
вывести текущую компоненту сильной связности |
|||
== Время работы == |
== Время работы == |
||
Алгоритм имеет временну́ю сложность <math>\mathrm{O}(|V|+| |
Алгоритм имеет временну́ю сложность <math>\mathrm{O}(|V|+|A|)</math>, где <math>|A|</math> — количество дуг, а <math>|V|</math> — вершин графа{{sfn|Джереми Сик и др.|2006}}. |
||
== См. также == |
== См. также == |
Версия от 07:43, 30 сентября 2022
Алгоритм Тарьяна — алгоритм поиска компонент сильной связности в орграфе, работающий за линейное время.
Этот алгоритм основан на том, что:
- Вершины рассматриваются в обратном топологическом порядке, поэтому в конце рекурсивной функции для исходной вершины не будет встречено ни одной вершины из той же компоненты сильной связности, так как все вершины, достижимые из исходной, уже обработаны.
- Обратные связи в дереве дают второй путь из одной вершины в другую и связывают компоненты сильной связности в одну.
Неформальное описание алгоритма
Алгоритм Тарьяна можно понимать как вариацию алгоритма поиска в глубину, в котором при посещении вершины и окончании обработки вершины выполняются дополнительные действия. Посещение вершины происходит при движении от корня к листьям, а окончание обработки вершины — на обратном пути. При посещении вершины она проталкивается во вспомогательный стек, при окончании обработки компоненты сильной связности все её вершины выталкиваются из этого стека[1].
Алгоритм в псевдокоде
// входные данные: ориентированный граф G = (V, A) // выходные данные: множество компонент сильной связности index := 0 S := empty stack for each v in V do if v.index is undefined then strongconnect(v) function strongconnect(v) // В index храним количество ранее обработанных вершин, v.index - это "время входа" в вершину v v.index := index v.lowlink := index index := index + 1 S.push(v) // Поле onStack нужно, чтобы проверять принадлежность вершины стеку за O(1) v.onStack := true // Перебираем дуги, исходящие из v for each (v, w) in A do if w.index is undefined then // Вершина w ранее не посещалась; запустимся из неё рекурсивно strongconnect(w) v.lowlink := min(v.lowlink, w.lowlink) else if w.onStack then // Вершина w находится в стеке S, значит, принадлежит той же компоненте сильной связности, что и v // Если w не в стеке, значит, дуга (v, w) ведёт в ранее обработанную компоненту сильной связности и должна быть проигнорирована // Замечание: в следующей строке намеренно используется w.index вместо w.lowlink - это отсылает к исходной статье Тарьяна // Если заменить w.index на w.lowlink, алгоритм останется корректным v.lowlink := min(v.lowlink, w.index) // Вершина v - корень текущей компоненты сильной связности, все верхние вершины стека образуют эту компоненту if v.lowlink = v.index then создать новую компоненту сильной связности repeat w := S.pop() w.onStack := false добавить w в текущую компоненту сильной связности while w ≠ v вывести текущую компоненту сильной связности
Время работы
Алгоритм имеет временну́ю сложность , где — количество дуг, а — вершин графа[1].
См. также
Алгоритм поиска компонент сильной связности с двумя стеками — аналогичный алгоритм, использующий поиск в глубину.
Примечания
Ссылки
Литература
- Tarjan R. E. Depth-first search and linear graph algorithms (англ.) // SIAM Journal on Computing. — 1972. — Vol. 1, no. 2. — P. 146–160. — doi:10.1137/0201010.
- Роберт Седжвик. Алгоритмы на графах = Graph algorithms. — 3-е изд. — Россия, Санкт-Петербург: «ДиаСофтЮП», 2002. — С. 496. — ISBN 5-93772-054-7.
- Джереми Сик, Лай-Кван Ли, Эндрю Ламсдэйн. C++ Boost Graph Library. — Питер, 2006. — С. 202—205. — 304 с. — ISBN 5-469-00352-3.