- •Глава VII
- •§ 43. Эйлеровы графы
- •Глава VIII
- •§ 45. Графическая последовательность
- •§ 46. Критерии графичности последовательности
- •§ 47. Реализация графической последовательности с максимальной связностью
- •§ 48. Гамильтонова реализация графической последовательности
- •§ 49. Расщепляемые графы
- •§ 50. Пороговые графы
- •§ 51. Пороговое разложение графа
- •§ 52. Степенное множество графа
- •Глава IX
- •§ 53. Правильная раскраска
- •§ 54. Оценки хроматического числа
- •§ 56. Раскраска ребер
- •§ 57. Связь матроидных разложений графов с раскрасками
- •§ 58. Раскраска планарных графов
- •§ 59. Проблема четырех красок
- •§ 60. Другие подходы к раскраске графов
- •§ 61. Совершенные графы
- •§ 62. Триангулированные графы
- •Глава X Ориентированные графы
- •§ 63. Основные определения
- •§ 64. Полустепени исхода и полустепени захода
- •§ 65. Обходы
- •§ 66. Пути
- •§ 67. База и ядро
- •Глава XI
- •§ 68. Основные определения и свойства
- •§ 69. Независимые множества
- •§ 70. Раскраски
- •§ 71. Реализации гиперграфа
- •Глава XII
- •§ 72. Предварительные сведения
- •§ 73. Поиск в глубину
- •§ 74. Отыскание двусвязных компонент
- •§ 75. Минимальный остов
- •§ 76. Кратчайшие пути
- •§ 77. Наибольшие паросочетания и задача о назначениях
- •§ 78. Труднорешаемые задачи
§ 74. Отыскание двусвязных компонент
В этом параграфе мы рассмотрим применение поиска в глубину к выделению 2-связных компонент графа. Здесь дело обстоит не так просто, как в предыдущей задаче. Конечно, можно было бы, удаляя поочередно вершины графа и каждый раз выделяя связные компоненты, найти его точки сочленения и блоки. Такой подход, однако, приводит к алгоритму сложности по крайней мере O(|EG|*|G|)Использование более глубоких свойств ПГ позволяет получить линейный по сложности алгоритм решения этой задачи.
В дальнейшем удобно использовать следующие термины. Пусть D=(V, А)—ориентированное дерево, х, у € V. Назовем х отцом вершины у, а у — сыном вершины х, если дуга (х, у) принадлежит А. Будем говорить, что вершины хиу сравнимы, если одна из них достижима из другой. Если при этом у достижима из х, то х — предок вершины у, а у — потомок вершины х. Подграф графа D, порожденный множеством, состоящим из вершины у и всех ее потомков, будем обозначать через Dv и называть поддеревом с корнем у.
Пусть в графе G проделан поиск в глубину из вершины v0 и получены остовное глубинное дерево Т и множество обратных ребер В.
Следующие три утверждения дают теоретическую основу для разработки эффективного алгоритма выделения двусвязных компонент.
Утверждение 74.1. Если дуга (х, у) принадлежит В, то х является потомком вершины у в Т.
Надо доказать, что вершина х принадлежит поддереву Ту. Предположим противное. Согласно утверждению 73.2 вершина х получает свой ПГ-номер позже, чем у. Поэтому присвоение вершине х ПГ-номера произойдет не раньше, чем будут посещены все вершины дерева Ту и произойдет возвращение в вершину s = F(y). Но возвращение в s не может произойти прежде, чем все вершины множества Ny получат ПГ-номера. Поскольку х € Ny и ПГ-номера к этому моменту не имеет, то получаем противоречие.
С ледующие два утверждения показывают, каким образом поиск в глубину «реагирует» на точки сочленения графа.
Утверждение 74.2. Вершина vo является точкой сочленения графа G тогда и только тогда, когда она имеет не менее двух сыновей.
Пусть v0 — точка сочленения графа G. Ясно, что каждый блок графа, включающий вершину v0, содержит по крайней мере одного из ее сыновей.
Пусть теперь s1 , s2, ..., sk — сыновья вершины v0. Рассмотрим поддеревья TSi (i = 1, k). Ясно, что VG— v0 = U VTSi. Для доказательства несвязности графа G — v0 достаточно показать, что в этом графе нет ребер, связывающих вершины различных TSi. Но это сразу следует из утверждения 74.1.
Будем говорить, что х — собственный предок (потомок) вершины у, если х — предок (потомок) у и х ≠ у.
Утверждение 74.3. Вершина v≠v0 является точкой сочленения графа G тогда и только тогда, когда для некоторого сына s этой вершины не существует дуги (х, у) € В такой, что х — потомок (не обязательно собственный) вершины s, а у — собственный предок вершины v.
Пусть v — точка сочленения графа G и G1, G2, ..., Gm, m ≥ 2,— блоки этого графа, содержащие вершину v. Пусть, далее, v' = F(v), т. е. v' — отец вершины v.Не теряя общности считаем, что вершина v' содержится в блоке G1. Каждый из остальных блоков Gi(i = 2, m)должен, очевидно, содержать хотя бы одну вершину, являющуюся сыном вершины v. Пусть, например, s — сын вершины v и s € G2. Если теперь допустить существование «обратного» ребра ab (т. е. дуги (а, b)€ В) такого, что а — потомок s, а b — собственный предок вершины v,то придем к существованию в графе G простого цикла,содержащего вершины v' и s. Этот цикл образован простой (а,b)-цепью, состоящей из «прямых» ребер и «обратного» ребра ab. Это означает (согласно утверждению 36.3), что вершины s и v' принадлежат одному блоку. Получили противоречие.
Докажем теперь вторую часть утверждения. Пусть вершина s является сыном вершины v, для которого выполняется условие, фигурирующее в формулировке утверждения. Это условие вместе с утверждением 74.1 означает, что если ab—«обратное» ребро и а € Та, то либо b € Та, либо b = v. Последнее означает, что все ребра графа G, связывающие вершины множества VTа с вершинами VG\VTа, инцидентны вершине v, т. е. v-—точка сочленения графа G.
Перейдем теперь непосредственно к разработке алгоритма выделения блоков графа. Чтобы уяснить схему применения ПГ к решению этой задачи, обратимся к примеру.На рис.74.1 изображен граф «с точностью до блоков». ПГ начинается в вершине v0. После нескольких шагов придем в одну из точек сочленения графа, например, в c2. Пусть, далее, выбирается и помечается как «прямое» ребро c2x блока В4. После этого дальнейшая работа ПГ вплоть до возвращения в c2 происходит точно так, как если бы было v0 = c2 и блоков В1, В2, В3 в графе G не было бы вовсе. Поэтому возвращение в Сг из х будет означать, что пройдены все ребра блоков В4 , В5, В6.,В7 .Таким образом, возвращение в c2 произойдет позже, чем возвращения в c3 и c4 из висячих блоков В5, В6.,В7. Эти рассмотрения приводят к следующему важному выводу. Самое первое возвращение в точку сочленения произойдет сразу же после завершения обхода всех ребер некоторого висячего блока Вк. Это же справедливо и по отношению к дальнейшему поведению ПГ в графе, полученном из графа G удалением блока Bh.
Покажем, как использовать сказанное выше в предположении, что у нас есть способ, позволяющий при каждом возвращении из вершины v в F(v) определять, является ли F(v) точкой сочленения. С этой целью заведем стек S, в который будем заносить всякое ребро графа G сразу после получения им пометки «прямое» или «обратное». Таким образом, все ребра добавляются в конец списка S. Пусть в нашем примере возвращение из вершины у в c4 (см. рис. 74.1) является самым первым возвращением в точку сочленения. Тогда к моменту возвращения в c4 ребра блока В6 будут занимать все t последних мест в стеке S, где t — число ребер этого блока. Важно при этом, что ребро c4 у занимает первое среди t указанных мест. Это позволяет, просматривая стек S справа налево, выделить (т. е., например, переместить з отдельный список) все ребра блока В6. Затем эти ребра удаляются из S.
Итак, учитывая сказанное, необходимо уметь в процессе ПГ быстро определять возвращение в вершину, являющуюся точкой сочленения. Утверждения 74.2 и 74.3 дают соответствующие критерии, и нам надо их «алгоритмизировать». С этой целью для каждой вершины v € VG шределим множество P(v). В это множество включим вершину v и каждого ее предка w, для которого существует «обратное» ребро xw такое, что х — потомок вершины v в остовном глубинном дереве Т. Иными словами, множество P(v) состоит из всех предков вершины v, которых можно достичь из v, проходя сначала несколько (возможно, ни одной) дуг дерева Т, а затем одну дугу множества В.
Введем теперь функцию l(v), v € VG, полагая l(v) = min ПГ(x). Например, в графе на рис. 73.1 l(l) = 1, l(2) = 1, l(3) = l, l(4) = 3, l(5) = 1, l(6)=3, l(7)=3.
Используя функцию l(v), сформулируем утверждение 74.3 в следующем виде, более удобном для организации вычислений.
Утверждение 74.4. Вершина v≠v0 является точкой сочленения графа G тогда и только тогда, когда существует такой сын s этой вершины, что l(s)≥ ПГ(v).
В ычислить значение l(v) нетрудно, если известны значения функции l для всех сыновей вершины v. Именно, если s1, s2 , ..., sm — сыновья вершины v, то имеет место формула
Справедливость этого соотношения становится очевидной, если заметить следующее. Множество предков вершины v, достижимых из нее с использованием дуг дерева Т и не более одной дуги из В, состоит из предков v, достижимых таким же способом из вершин s1, s2 , ..., sm и множества вершин, к которым ведут обратные ребра от вершины v.
Используя соотношение (1), функцию l можно вычислять попутно с выполнением обычных операций поиска в глубину. При первом посещении вершины v вместе с присвоением ПГ-номера полагаем l(v) = ПГ(v). В дальнейшем это значение корректируется в соответствии с формулой (1) следующим образом. Всякий раз, когда происходит возвращение в вершину v из некоторого ее сына s, полагаем l(v):=min{l(s), l(v)}. Кроме того, когда некоторое ребро vw помечается как «обратное», полагаем l(v):=min{l(v), ПГ(w)}. К моменту возвращения из v в вершину x=F(v) будет вычислено истинное значение l(v), которое в дальнейшем используется для корректировки l(х). Существенно, что каждая из этих корректировок требует только O(1) дополнительного времени. Поэтому ПГ вместе с вычислением функции l по-прежнему будет выполняться за время O(|EG|+|G|).
Еще одна добавка к «стандартному» поиску в глубину связана с точками сочленения. Для обнаружения точки сочленения достаточно после каждого возвращения в вершину v из некоторого ее сына s сравнить величины l(s) и ПГ(v). Если окажется, что l(s) ≥ ПГ(v), то все ребра стека S, начиная с последнего и кончая sv, удаляются из этого списка. Удаленные ребра составляют один из блоков графа G. Согласно утверждению 74.4 неравенство l(s) ≥ ПГ(v) означает, что либо вершина v — точка сочленения, либо v = v0 и v не является точкой сочленения. Заметим, что второй случай не требует особого рассмотрения. В этом случае удаленные из стека S ребра соответствуют единственному блоку графа, содержащему v0. И, наконец, последняя деталь. Выделение блока графа G мы понимаем как удаление всех ребер этого блока из стека S. Можно считать, что одновременно с удалением из S каждое такое ребро заносится в некоторый другой список, причем множество ребер разных блоков разделены в этом списке, например, символом 0. Процедура построения этого нового списка очевидна, и чтобы не загромождать описания алгоритма, мы не приводим ее в явном виде.
Сравнение l(s) с ПГ(v) производится |G| — 1 раз, и, следовательно, суммарное время, затраченное на выполнение сравнений, составляет O(|G|). Каждое ребро графа один раз включается в стек S и один раз исключается из него. Поэтому вся работа, связанная с изменением S, выполняется за время O(|EG|). Таким образом, поиск в глубину вместе с выделением блоков будет выполняться за время O(|EG|+|G|).
Алгоритм A2 поиска в глубину с выделением двусвязных компонент.
1.ПГ(v0):=1, l(vo):=1, S:=¢, F(v0):=0, T:=¢, В:= ¢, k :=1,p:= 1, Q(1):= v0.
2.v:=Q(p).
3.Просматривая список Nv, найти такую вершину w,что ребро vw не помечено, и перейти к п. 4. Если таких вершин нет, то перейти к п. 5.
Если вершина w имеет ПГ-номер, то поместить ребро vw как «обратное», занести ребро vw в стек S, l(v): = min{l(v), ПГ(w)} [выполнена корректировка l(v)].Перейти к п. 3 и продолжить просмотр списка Nv. Иначе ребро vw пометить как «прямое», k := k + 1, ПГ(w):=k,l(w):=k, F(w):= v,p:=p + 1, Q(p):= w. Перейти к п. 2.
5. р := р — 1. Если р = 0, то конец. Иначе перейти к п. 6.
6.Пусть x = F(v). Если l(v)> ПГ(x), то перейти к п. 7. Иначе l(x):= {l(x), l(v)} [выполнена корректировка 1{х)] и перейти к п. 2.
7.Удалить из стека S все ребра вплоть до xv [удаленные ребра составляют блок графа G]. Перейти к п. 2.
Пример. На рис. 74.2 изображен граф G и приведены списки смежности его вершин. Этот граф имеет четыре блока В1 , В2 , В3 , В4 и две точки сочленения d и х. Поиск в глубину начинается с вершины α, т. е. vo=α. На рис. 74.3 отражена ситуация, сложившаяся непосредственно перед выделением блока В4. К этому моменту шесть ребер помечены как «прямые» и одно как «обратное». Прямые ребра проведены жирными линиями, а обратное — пунктирной. Тем и другим придана соответствующая ориентация. Помеченные ребра расположены в стеке S в том порядке, в котором они получали пометки. Пара чисел (α, β), приписанная вершине v, имеет следующий смысл: α = ПГ(v), a β — значение l(v), вычисленное к рассматриваемому моменту.
П осле того, как ребро tx получило пометку «обратное», произошло возвращение в вершину z. При этом сравнение величин ПГ(z)=6 и l(t) = 5 показало, что вершина z не является точкой сочленения. Далее при возвращении
из вершины z в х обнаруживается, что l(z) = 5 = ПГ(x). Следовательно, все ребра от tx до xz в стеке S составляют блок графа G. Эти ребра —tx, tz, xz — удаляются из S, и тем самым выделен блок В4..
После этого алгоритм работает так, как если бы блока В4. в графе G не было вообще. Ключевые моменты
д альнейшей работы алгоритма иллюстрируются на рис. 74.4. Каждый из трех графов (вместе с их пометками и стеком S), изображенных на этом рисунке, отражает ситуацию, создавшуюся непосредственно перед удалением очередного блока. Выделение последнего блока, т.е. удаление его ребер из стека S, произойдет при возвращении в вершину v0 = а, для которой ПГ (а) = 1 =l(с). Таким образом, на вершину vo — a алгоритм реагирует» точно так же, как на точку сочленения.
В заключение отметим, что поиск в глубину оказывается полезным и при отыскании 3-связных компонент графа. Известен основанный на поиске в глубину алгоритм, решающий эту задачу за время O(|EG|+|G|).