На этом шаге мы рассмотрим алгоритм отыскания компонент сильной связности в орграфе.
Пусть G = (V, Е) - связный орграф. Алгоритм 1 легко приспособить для организации поиска в глубину в орграфе G. Для этого нужно лишь список list(v) заменить списком list(v'), состоящим из концов всех дуг, выходящих из вершины v. Все понятия, связанные с поиском в глубину в обыкновенном графе, очевидным образом применимы и для орграфов. В частности, в результате поиска в глубину в орграфе G строится d-лес (глубинный лес), состоящий из ориентированных корневых деревьев. Тем самым, на множестве вершин орграфа G вводится отношение частичного порядка: u < v, если u, v принадлежат одной компоненте связности d-леса и u является потомком v. Отметим также, что поиск в глубину разбивает множество всех дуг орграфа на четыре класса:
На рисунке 1 показан орграф и его глубинный лес.
Рис.1. Ортграф и глубинный лес
Следующие два утверждения очевидны.
Применим поиск в глубину для построения алгоритма, способного распознавать сильную связность орграфа G. На самом деле будет построен алгоритм, который сможет в орграфе G выделять компоненты сильной связности, являющиеся максимальными сильно связными подграфами орграфа G. Заметим, что компоненты сильной связности являются классами отношения взаимной достижимости на множестве вершин орграфа G.
Пусть Gi = (Vi Ei) (1 ≤ i ≤ k) - компоненты сильной связности орграфа G. Обозначим через ri такую вершину компоненты Gi (1 ≤ r ≤ k), что num[ri] = min num(vi). Пусть r - преобразование множества вершин орграфа G, определенное правилом: r(w) = ri для любой вершины w ∈ Gi. Ясно, что вершины u и w лежат в одной компоненте сильной связности тогда и только тогда, когда r(u) = r(w).
Пусть q = 1. Тогда существует дуга r(w)w. Ясно, что эта дуга древесная или прямая, поэтому w - потомок r(w).
Допустим, что q > 1. Обозначим через х предпоследнюю вершину орцепи Р. Легко проверяется, что вершины х и r(w) взаимно достижимы, поэтому х и r(w) лежат в одной компоненте сильной связности. Следовательно, к вершине х применимо предположение индукции. Таким образом, вершина х - потомок r(w). Если xw - древесная или прямая дуга, то w < х и потому r < r(w). Пусть xw является обратной или поперечной дугой. Применяя лемму 1, получаем, что num[w] < num[х]. Так как num[r(w)] < num[w] и x - потомок r(w), вершины r(w), w, х удовлетворяют условиям леммы 2, т.е. w < r(w).
Пусть v - произвольная вершина орграфа G. Через v обозначим поддерево d-леса с корнем v. Если X ⊆ v, то множество всех таких вершин w, что r(w) > v и для некоторой вершины х ∈ X существует обратная или поперечная дуга xw, будем обозначать, как и раньше, через VB(X, v).
Леммы 3 и 4 показывают, что вершины каждой компоненты сильной связности d, 1 ≤ i ≤ k, образуют в глубинном лесе поддерево с корнем ri.
Предположим, что v ≠ r(v). Из леммы 3 следует, что v < r(v). С другой стороны, существует (v, r(v))-орцепь Р. В орцепи P найдем первую дугу xw такую, что w не принадлежит v. Очевидно, xw - обратная или поперечная дуга. Кроме того, легко проверяется, что w и v лежат в одной компоненте сильной связности орграфа G. Следовательно, r(w) = r(v) > v. Таким образом, w ∈VB(v, v), откуда вытекает, что VB(v, v) ⊆ v.
На множестве V вершин графа G рассмотрим следующую функцию
L[v] = min num({v} ∪ VB(v, v)).
Предположим, что v ≠ r(v). Учитывая теорему 4, получаем, что существует w ∈ VB(v, v)\v. Найдется такая вершина w ∈ v, что xw - обратная или поперечная дуга. Следовательно, num[w] < num[х]. Проверим, что num[w] < num[v]. Рассуждая от противного, допустим, что num[v] ≤ num[w]. Поскольку w ≠ v, имеем строгое неравенство num[v] < num[w]. К вершинам v, w, x можно применить лемму 2, поэтому w ∈ v, что невозможно. Из доказанного неравенства следует, что L[v] < num[w] < num[v].
Теорема 5 позволяет распознавать в каждой компоненте сильной связности вершины v, удовлетворяющие условию v = r(v). Сравнивая теорему 5 с теоремой 2, мы видим, что роль таких вершин для орграфов аналогична роли точек сочленения для обыкновенных графов.
Перейдем к описанию алгоритма, позволяющего строить компоненты сильной связности орграфа G. Пусть к орграфу применен поиск в глубину. В компонентах сильной связности Gi, 1 ≤ i ≤ k, ранее были выделены корневые вершины ri. С этого момента будем считать, что компоненты сильной связности занумерованы следующим образом: если 1 ≤ i < j ≤ k, то вершина ri использована поиском в глубину раньше, чем вершина rj. Такая нумерация компонент сильной связности обладает следующим важным свойством.
Из леммы 7 легко извлекается следующий способ нахождения компонент сильной связности. Определим стек S и будем помещать в него вершины орграфа G в том порядке, в каком их просматривает поиск в глубину. Если сразу после того, как вершина гi использована, вытолнуть из стека S все вершины до ri включительно, мы получим компоненту Gi.
Теперь мы можем привести формальное описание алгоритма для отыскания компонент сильной связности орграфа G.
Алгоритм 3.
1. procedure StrongComp(v); 2. begin 3. num[v]:= i; L[v]:= i; i:= i+1; S <= v; 4. for w ∈ do 5. if num[w] = 0 then 6. begin 7. StrongComp(w); L[v]:= min(L[v], L[w]); 8. end 9. else if num[w] < num[v] and w ∈ S then 10. L[v]:= min(L[v], num[w]); 11. if L[v] = num[v] then 12. while num[top(S)] ≥ num[v] do 13. begin 14. x <= S; {добавить x к очередной компоненте сильной связности;} 15. end 16. end; 17. begin 18. i:= 1; S:= nil; 19. for v ∈ V do num[v]:= 0; 20. for v ∈ V do 21. if num[v] = 0 then StrongComp(v) 22. end.
Процедура StrongComp(v) является модифицированной версией рекурсивной процедуры DFS(v) алгоритма 1. Для данной вершины v значение функции L[v] вычисляется циклом в строках 4-10 в соответствии с формулой, полученной в лемме 6. В строке 11 для вершины r проверяется условие из теоремы 5, означающее, что v - корневая вершина очередной компоненты сильной связности. Для нахождения компонент сильной связности использован упомянутый выше стек S, элементами которого являются вершины орграфа G (строки 12-15). Кроме того, этот стек участвует в формировании условия, проверяемого в строке 9. Выполнение этого условия означает, что w ∈ VB(v, v).
Мы изложили неформальные соображения по поводу правильности работы алгоритма 3. Перейдем к строгим рассуждениям.
Покажем, что в процессе выполнения процедуры StrongComp(r1) условие L[r1] = num[r1] будет выполнено. Напомним, что множество r1 совпадает с множеством вершин компоненты G1. Отсюда следует, что VB(u, u) ⊆ VB(r1, r1) для любого потомка u вершины r1. Из теоремы 4 вытекает, что VB(r1, r1) ⊆ r1. При выполнении процедуры StrongComp(u) все просмотренные вершины находятся в стеке S. Поэтому значения L[u], уточняемые в строках 7 и 10, всегда будут не меньше, чем num[r1]. С учетом присваиваний в строке 3, имеем L[r1] = num[r1].
Для завершения доказательства применим индукцию по числу q компонент сильной связности орграфа G.
Пусть q = 1. Тогда G = G1, т. е. все вершины орграфа G, отличные от вершины r1, являются ее потомками. Условие в строке 11 выполнится только один раз при v = r1. Поэтому из стека S будут вытолкнуты все вершины орграфа G.
Предположим, что q > 1. Пусть алгоритм 3 начинает работу с вершины v0. В процессе работы алгоритма после завершения работы процедуры DFS(r1) из стека S будут вытолкнуты все вершины компоненты сильной связности G1. Обозначим через G' подграф, полученный из орграфа G удалением компоненты d. Очевидно, к орграфу G' применимо предположение индукции. Следовательно, все компоненты, отличные от G1, будут найдены правильно.
На следующем шаге мы рассмотрим поиск в ширину.