Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кратч пути1 .doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
2.98 Mб
Скачать

Лекция 4. Поиск в глубину в графе

Ягодка за ягодкой… (Русские народные сказки)

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

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

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

Опишем классический метод поиска в неориентированном графе — метод ПОИСКА В ГЛУБИНУ (англ. depth first search).

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

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

Если же у вершины v НЕТ ни одной НОВОЙ «соседки», то говорим, что v ИСПОЛЬЗОВАНА, и возвращаемся на ШАГ НАЗАД — в вершину, из которой попали в v. Просмотренные, но еще не использованные вершины накапливаются в стеке. Использованные вершины удаляются из стека. Когда стек опустеет, то у связного графа будут просмотрены все вершины, у несвязного — компонента связности, содержащая вершину k.

Рис. 10

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

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

Переменная НОВЫЙ — глобальная.

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

2 begin рассмотреть v; НОВЫЙ[v]:=ложь;

3 for u ЗАПИСЬ[v] do {перебор всех соседей v}

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

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

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

1 begin

2 for v V do НОВЫЙ[v]:=истина ; {инициализация}

3 for v 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 GL1(v); {поиск в глубину,начиная с v}

2 begin

3 СТЕК:=NIL; СТЕК<=v;

4 рассмотреть v;

5 НОВЫЙ[v]:=ложь; {v помещается в СТЕК}

6 while СТЕК do

7 begin

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

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

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

10 else b:=not НОВЫЙ [НАЧАЛО[verh]^.uzl];

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

11 while b do

12 begin

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

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

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

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

15 else b:=not НОВЫЙ[НАЧАЛО[verh]^.uzl]

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

16 end;

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

18 begin

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

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

21 рассмотреть verh; НОВЫЙ[verh]:=ложь

22 end

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

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

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

26 end; {procedure GL1}

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

Ответы.

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