Это в лекции 3. |
Три алгоритма на графах
Поиск в глубину и задача о лабиринте
Задача поиска выхода из лабиринта известна с древних времен. В терминах графов ее можно формализовать так: лабиринт - это неориентированный граф, вершины которого представляют "перекрестки" лабиринта, а ребра - дорожки между соседними перекрестками. Одна или несколько вершин отмечены как выходы. Задача состоит в построении пути из некоторой исходной вершины в вершину-выход.
В этом разделе мы рассмотрим метод обхода всех вершин графа, называемый поиском в глубину. Его идею кратко можно описать так:
находясь в некоторой вершине v, идем из нее в произвольную еще не посещенную смежную вершину w, если такой вершины нет, то возвращаемся в вершину, из которой мы пришли в v.
Алгоритм поиска в глубину
Вход: G=(V, E) - неориентированный граф, представленный списками смежностей: для каждой список Lv содержит перечень всех смежных с v вершин.
Выход: NUM[v] - массив с номерами вершин в порядке их прохождения
и множество (древесных) ребер , по которым осуществляется обход.
Алгоритм ПОГ
- T = {}; NOMER = 1;
- для каждой вершины
положим NUM[v] = 0 и пометим v как "новую";
-
ПОКА существует "новая" вершина
- ВЫПОЛНЯТЬ ПОИСК(v).
Основную роль в этом алгоритме играет следующая рекурсивная процедура.
Алгоритм ПОИСК(v):
1. пометить v как "старую"; 2. NUM[v] = NOMER; NOMER = NOMER + 1; 3. ДЛЯ КАЖДОЙ w принадлежащей Lv ВЫПОЛНЯТЬ 4. ЕСЛИ вершина w "новая" 5. ТО 6. { добавить (v, w) к T; 7. ПОИСК(w); 8. }
Теорема 11.2. Алгоритм ПОГ обходит (нумерует) все вершины графа G=(V,E). Если G - связный граф, то S=(V,T) - это остов G, если граф G не является связным, то S=(V,T) - это остовной лес для G, т.е. объединение остовных деревьев для каждой из компонент связности G.
Доказательство Первое утверждение следует из того, что по окончании алгоритма ПОГ все вершины графа старые, а это значит, что для каждой из них вызывалась процедура ПОИСК, которая в стр.2 присвоила номер.
Заметим теперь, что если ребро (v, w) попадает в T, то вызов процедуры ПОИСК(w) происходит после вызова ПОИСК(v) и поэтому NUM[v] < NUM[w]. Существование цикла в S означало бы, что для некоторого ребра из T это свойство нарушено (почему?). Следовательно, в S циклов нет.
Пусть G1=(V1,E1) - связная компонента G и - первая ее вершина,
для которой вызывается процедура ПОИСК. Тогда для каждой вершины
внутри вызова ПОИСК(v1) произойдет вызов ПОИСК(w).
Это утверждение доказывается индукцией по расстоянию ( длине кратчайшего пути ) от v1 до w.
Если это расстояние равно 1, то и рассматривается в
вызове ПОИСК(v1) в стр.3.
Если w в
этот момент "старая", то, значит, ПОИСК(w) уже вызывался. Если же w
"новая", то в стр. 7 происходит вызов ПОИСК(w).
Предположим теперь, что ПОИСК(u) вызывается для всех вершин u, находящихся на расстоянии k >= 1 от v1, и пусть вершина
находится на рсстоянии (k + 1) от v1. Тогда имеется путь длины (k + 1) от v1
до w . Пусть u - это предпоследняя вершина на этом пути. Тогда расстояние от v1 до u равно k и по нашему предпроложению в некоторый момент выполняется вызов ПОИСК(u). Так как
, то в этом вызове вершина w в некоторый момент
рассматривается в цикле в стр.3. Как и выше,
если она в этот момент "старая", то ПОИСК(w) уже вызывался. Если же w еще "новая", то в стр.7 происходит вызов ПОИСК(w).
Также по индукции замечаем, что если вызов ПОИСК(w) произошел внутри вызова ПОИСК(v), то в T имеется путь из v в w. Следовательно, граф S1=(V1,T1), построенный в процессе вызова ПОИСК(v) является деревом с корнем v.
Дерево S=(V,T), которое строится алгоритмом ПОГ, называется глубинным остовом или глубинным остовным деревом графа G. Ребра, попавшие в множество T, называются прямыми, а не попавшие в это множество ребра из множества (E \ T) - обратными. Каждое обратное ребро (v,w) соединяет вершину v с ее предком w в глубинном остове (см. задачу 11.8). Поэтому в исходном графе G оно определяет цикл: от w к v по ребрам дерева T, а затем обратно от v к w по ребру (v,w). Поэтому алгоритм ПОГ можно использовать для проверки наличия циклов в G. Ребро (v,w) не добавляется к T, т.е. является обратным, тогда и только тогда, когда в стр.4 вызова ПОИСК(v) обнаруживается, что вершина w "старая". Поэтому, добавив в процедуру ПОИСК(v) последнюю строку
9. ИНАЧЕ ПЕЧАТЬ(v,w),
мы получим процедуру, которая в дополнение построению к глубинного остова графа будет распечатывать список всех обратных ребер. Если этот список не пуст, то в графе имеются циклы, иначе - нет.
Поиск в глубину позволяет обнаруживать не только циклы, но и многие другие важные части графов.
Определение 11.3. Ребро (v,w) неориентированного графа G=(V,E) называется мостом G, если при его удалении из E число связных компонент графа увеличивается, т.е. в графе G'=(V, E \ { (v,w)} связных компонент больше, чем в G.
Из этого определения, в частности, следует, что ребро является мостом тогда и только тогда, когда оно не входит ни в какой цикл ( почему?). Поиск в глубину позволяет находить все мосты графа. Во-первых, заметим, что любой мост (v,w) является ребром глубинного остова, так как другого пути, связывающего v и w нет. Во-вторых, если это ребро ориентировано от v к w, то в глубинном остове нет обратных ребер, соединяющих w и его потомки с предками w. Это условие является и достаточным, так как, если таких ребер нет, то удаление (v,w) нарушит связь между v и w и они окажутся в разных компонентах связности. Обозначим через ВЕРХ(w) минимум из NUM[w] и наименьшего из номеров вершин, к которым ведут обратные ребра от вершин поддерева Tw. Тогда, учитывая, что обратные ребра соединяют потомков с предками и что номера предков меньше номеров потомков, предложенный критерий можно переформулировать в следующем виде.
Теорема 11.3. Ребро (v,w) глубинного остова D=(V,E) неориентированного графа G=(V,E) является мостом G тогда и только тогда, когда ВЕРХ(w) > NUM[v] или, что эквивалентно, ВЕРХ(w) = NUM[w].
Вычисление значения ВЕРХ(w) можно организовать в процессе обхода в глубину, используя следующее соотношение:
![ВЕРХ(w)= \min \{ \{NUM[w]\} \cup
\{\textrm{ВЕРХ}(z)\ |\ (v,z) - \textit{ прямое ребро }\}\cup \\ \cup \{NUM[u] | (w,u) - \textit{ обратное ребро }\} \}](/sites/default/files/tex_cache/f30736517c46258ec8d158645cea5f96.png)
Для этого достаточно в строку 2 алгоритма ПОГ добавить начальное присвоение ВЕРХ(v) := NUM[v], в строке 7 приписать после ПОИСК(w) присвоение ВЕРХ(v) := min {ВЕРХ(v), ВЕРХ(w)}, учитывающее прямые ребра, и добавить строку
9. ИНАЧЕ ВЕРХ(v) := min{ВЕРХ(v),NUM(w)}
Зная значения ВЕРХ(w), нетрудно выявить все мосты, используя критерий из теоремы 11.3.
Пример 11.2. Применим алгоритм ПОГ к графу G2, изображенному на рис. 11.3.
Его представление в виде списков смежности имеет следующий вид:
![\begin{array}{ll}
L_1:\ 6, 2 & L_7: 6, 8\\
L_2:\ 1, 9, 10, 3 & L_8: 6, 7\\
L_3:\ 2, 5, 4 & L_9: 2, 10, 11\\
L_4:\ 3, 5 & L_{10}: 2,9, 11 \\
L_5:\ 2, 3, 4 & L_{11}: 9, 10\\
L_6:\ 1, 7, 8 &
\end{array}](/sites/default/files/tex_cache/78f08d81299bdef5cdc5ac05299c5fa2.png)
Алгоритм ПОГ вызовет процедуру ПОИСК(1). Эта процедура рекурсивно вызовет ПОИСК(6) и т.д. Вот структура всех получающихся вызовов процедуры ПОИСК:
![\begin{array}{ccccccc}
ПОИСК(1) &\Rightarrow& ПОИСК(6) &\Rightarrow& ПОИСК(7) &\Rightarrow& ПОИСК(8)\\
\Downarrow& & & & & &\\
ПОИСК(2) &\Rightarrow& ПОИСК(9) &\Rightarrow& ПОИСК(10) &\Rightarrow& ПОИСК(11)\\
\Downarrow& & & & & &\\
ПОИСК(3) &\Rightarrow& ПОИСК(5) &\Rightarrow& ПОИСК(4) & &\\
\end{array}](/sites/default/files/tex_cache/ad6622127649ada4c9de616c473dd3fd.png)
Вначале идут "горизонтальные" вызовы, затем возвраты справа налево и вызовы "по вертикали". В результате вершины G2 получат следующие номера, отражающие порядок их прохождения:
![\begin{array}{rccccccccccc}
V : & 1 & 2& 3& 4& 5& 6& 7& 8& 9& 10 & 11\\
NUM :& 1 & 5& 9& 11& 10& 2& 3& 4& 6& 7 & 8
\end{array}](/sites/default/files/tex_cache/1140d91a92b6ccee35149f23dc91578f.png)
Ребра остова , построенные в процессе обхода графа, показаны на рис. 11.4.
Стрелки указывают направление обхода. В скобках рядом с номером вершины указан
ее номер в массиве NUM, т.е. номер в порядке обхода алгоритмом ПОГ.
В процессе построения этого дерева были определены следующие обратные ребра: (8,6), (10,2), (11,9), (5,2) и (4,3). Нетрудно проверить, что добавление любого из этих ребер к T приводит к образованию простого цикла.
Используя расширенный вариант ПОГ с вычислением функции ВЕРХ, мы получим следующий результат:
![\begin{array}{rccccccccccc}
V : & 1 & 2& 3& 4 & 5 & 6& 7& 8& 9& 10 & 11\\
NUM :& 1 & 5& 9& 11& 10& 2& 3& 4& 6& 7 & 8 \\
\textrm{ВЕРХ} :& 1 & 5& 5& 9 & 5 & 2& 2& 2& 5& 5 & 6 \end{array}](/sites/default/files/tex_cache/5fb3e7b70d616c0df134c959ac0b15e1.png)
Так как ВЕРХ(2) =NUM[2] = 5 и ВЕРХ(6) =NUM[6] = 2, то по теореме 11.3 мостами графа G2 являются ребра (1,2) и (1,6) и других мостов у него нет.
Алгоритм поиска в глубину часто используется как основа для различных алгоритмов обработки графов. Вместо строки 6, в котрой вершина v получает номер NUM(v), можно вставить вызов любой процедуры, обрабатывающей информацию, связанную с этой вершиной (например, для задачи о лабиринте это может быть проверка того, что v является выходом из лабиринта). И тогда полученный вариант алгоритма обеспечит обработку всех вершин графа.