Поиск в глубину в графе

Существует много различных алгоритмов, в основе которых лежит систематический перебор вершин графа, при котором каждая вершина просматривается ровно один раз. Будем рассматривать такие методы обхода или поиска в графе, что:

а) алгоритм решения легко «погружается» в этот метод;

б) каждое ребро графа анализируется число раз, ограниченное константой.

Опишем классический метод поиска в неориентированном графе – метод поиска в глубину(depth first search) (рис. 8.8).

Общая идея следующая. Начинаем поиск с некоторой фиксированной вершины k (корня). Затем выбираем одну из смежных (соединенных ребрами) с ней вершин. Назовем эту вершину u. Далее процесс поиска повторяется от u.

Пусть на каком-то этапе мы находимся в вершине v. Если среди ее соседей есть хотя бы одна новая (еще непросмотренная) вершина w, то просматриваем ее. После этого w перестает быть новой, и дальнейший поиск продолжаем с w.

Если же у вершины v нет ни одной новой «соседки», то говорим, что v использована, и возвращаемся на шаг назад– в вершину, из которой попали в v. Просмотренные, но еще не использованные вершины накапливаются в стеке. Использованные вершины удаляются из стека. Когда стек опустеет, то у связного графа будут просмотрены все вершины, у несвязного – компонента связности, содержащая вершину k.

 
Поиск в глубину в графе - student2.ru

Рис. 8.8. Поиск в глубину из вершины 1

Вопрос. Когда следует прервать работу алгоритма, если мы ищем путь от 1 до 5? Где находится путь?

Поиск в глубину можно записать с помощью рекурсивной процедуры GL.

Логический массив НОВЫЙ – глобальная переменная.

1 procedure GL(v) {поиск в глубину из вершины v}

2 begin

3 НОВЫЙ[v]:= ложь;

3 for u Поиск в глубину в графе - student2.ru ЗАПИСЬ[v] do {перебор всех соседей v}

4 if НОВЫЙ[u] then GL[u]

5 end; {v использована}

Поиск в глубину в произвольном (необязательно связном) графе проводится согласно следующему алгоритму:

1 begin

2 for v Поиск в глубину в графе - student2.ru V do НОВЫЙ[v]:= истина ; {инициализация}

3 for v Поиск в глубину в графе - student2.ru V do {перебор всех вершин графа}

4 if НОВЫЙ[v] then GL(v)

5 end. {поиск окончен, все вершины просмотрены}

Покажем теперь, что этот алгоритм просматривает каждую вершину в точности один раз и сложность его порядка O(n+m).

Первый вызов GL(v) для некоторой вершины v влечет за собой просмотр всех вершин компоненты связности графа, содержащей v. Это обеспечивает цикл 3 процедуры GL: после просмотра v будет вызвана GL для всех ее новых соседей.

Каждая вершина просматривается ровно один раз – просматриваются только новые вершины, сразу после просмотра НОВЫЙ[v]:= ложь.

Гарантия того, что будут просмотрены все вершины – цикл 3 основного алгоритма.

Оценим теперь вычислительную сложность алгоритма.

Циклы 2 и 3 основного алгоритма содержат n шагов, не считая вызовы процедуры GL. Строка 2 процедуры GL для всех вызовов повлечет O(n) шагов. Полное число шагов цикла 3 процедуры GL для всех рекурсивных вызовов будет порядка m – числа всех ребер, так как от вершины к ее соседям мы двигаемся по ребрам.

Суммарная сложность алгоритма О(n+m).

В связи с важностью поиска в глубину для проектирования алгоритмов на графах представим также нерекурсивную версию процедуры GL.

Рекурсия устраняется стандартным способом: каждая просмотренная вершина помещается в СТЕК и удаляется из стека после использования.

1 procedure GL_1(v); {поиск в глубину,начиная с v}

2 begin

3 СТЕК:= NIL; СТЕК <= v; {v помещается в СТЕК}

4 НОВЫЙ[v]:= ложь;

5 while СТЕК do

6 begin

7 verh:= top(СТЕК); {читаем верхний элемент}

{будем искать первую новую «соседку» элемента verh}

8 if НАЧАЛО[verh]=nil then b:= ложь

9 else b:= NOT(НОВЫЙ[НАЧАЛО[verh]^.uzl]);

{НАЧАЛО[verh]^.uzl-номер первой вершины в списке
ЗАПИСЬ[verh]}

10 while b do

11 begin

12 НАЧАЛО[verh]:= НАЧАЛО[verh]^.next;

{переводим указатель НАЧАЛО на следующую запись в

списке ЗАПИСЬ[verh]}

13 if НАЧАЛО[verh]=nil then b:= ложь

14 else b:= NOT(НОВЫЙ[НАЧАЛО[verh]^.uzl])

{проверяем, будет ли вершина следующей записи
новой}

15 end;

16 if НАЧАЛО[verh]=nil then {найдена новая
вершина!}

17 begin

18 verh:= НАЧАЛО[verh]^.uzl;

19 СТЕК <= verh; {новая вершина помещена в СТЕК}

20 НОВЫЙ[verh]:= ложь

21 end

22 else {вершина verh использована}

23 verh <= СТЕК {удалить verh из СТЕКа}

24 end {while СТЕК <> NIL}

25 end; {procedure GL1}

Корректность процедуры GL_1 доказывается аналогично GL.

Ответ.Когда 5 попадет в стек. В стеке.

Наши рекомендации