Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 27.09.2006 | Доступ: свободный | Студентов: 3533 / 125 | Оценка: 4.44 / 4.11 | Длительность: 13:45:00
Специальности: Программист, Математик
Лекция 5:

Поиск в глубину

< Лекция 4 || Лекция 5: 123 || Лекция 6 >

DFS-дерево

Поиск в глубину можно применить для нахождения компонент связности графа или для построения каркаса точно таким же образом, как поиск в ширину. Понятия прямого и обратного ребра определяются так же, как в предыдущем разделе и так же доказывается, что прямые ребра при поиске в глубину образуют каркас графа. Для связного графа каркас, получаемый поиском в глубину, называется DFS-деревом. DFS-дерево рассматривается как корневое дерево с корнем в стартовой вершине a. Это дерево обладает особыми свойствами, на использовании которых основаны многочисленные применения метода поиска в глубину. Рассмотрим наиболее важное из этих свойств.

Относительно любого корневого остовного дерева все ребра графа, не принадлежащие дереву, можно разделить на две категории. Ребро назовем продольным, если одна из его вершин является предком другой, в противном случае ребро назовем поперечным. В примере на рис. 5.2 ребра каркаса выделены жирными линиями, корень - черным кружком. Обратные ребра показаны тонкими линиями, из них продольными являются ребра (1, 7), (2, 9), (3, 8), а поперечными - ребра (1, 2), (2, 5), (3, 5).


Рис. 5.2.

Теорема 1. Пусть G - связный граф, T - DFS-дерево графа G. Тогда относительно T все обратные ребра являются продольными.

Доказательство. Убедимся сначала, что после того, как стартовая вершина a помещена в стек, на каждом последующем шаге работы алгоритма последовательность вершин, хранящаяся в стеке, образует путь с началом в вершине a, а все ребра этого пути принадлежат дереву. Вначале это, очевидно, так. В дальнейшем всякий раз, когда новая вершина y помещается в стек, к дереву добавляется прямое ребро (x,y), причем вершина x находится в стеке перед вершиной y. Значит, если указанное свойство имело место до добавления вершины в стек, то оно сохранится и после добавления. Удаление же вершины из стека, конечно, не может нарушить этого свойства.

Пусть теперь (x,y) - обратное ребро. Каждая из вершин x и y в ходе работы алгоритма когда-либо окажется в стеке. Допустим, x окажется там раньше, чем y. Рассмотрим шаг алгоритма, на котором y помещается в стек. В этот момент x еще находится в стеке. Действительно, вершина исключается из стека только тогда, когда в ее окрестности нет непосещенных вершин. Но непосредственно перед помещением в стек вершина y является новой и принадлежит окрестности вершины x. Таким образом, вершина x лежит на пути, принадлежащем дереву и соединяющем вершины a и y. Но это означает, что вершина x является предком вершины y в дереве T и, следовательно, ребро (x,y) - продольное.

Таким образом, каркас, изображенный на рис. 5.2, не мог быть построен методом поиска в глубину. Кстати, он не мог быть построен и с помощью поиска в ширину (почему?).

Глубинная нумерация

Ввиду важности этого метода опишем еще два варианта алгоритма поиска в глубину. Первый из них - рекурсивный, и, как обычно, рекурсия дает возможность представить алгоритм в наиболее компактной форме. Для того чтобы алгоритм выполнял какую-то полезную работу, будем нумеровать вершины в том порядке, в каком они встречаются при обходе. Номер, получаемый вершиной x, обозначается через Dnum (x) и называется ее глубинным номером. Вначале полагаем Dnum (x)=0 для всех x. Это нулевое значение сохраняется до тех пор, пока вершина не становится открытой, в этот момент ей присваивается ее настоящий глубинный номер. Таким образом, нет необходимости в какой-либо специальной структуре для запоминания новых вершин - они отличаются от всех других нулевым значением Dnum. Переменная c хранит текущий номер. Рекурсивная процедура DFSR обходит одну компоненту связности, а алгоритм 1 обходит весь граф и присваивает вершинам глубинные номера.

Алгоритм 1. Поиск в глубину с вычислением глубинных номеров - рекурсивный вариант

  1. for x\in V do Dnum\left(x\right)
:=0
  2. c:=0
  3. for x\in V do
  4. if Dnum\left(x\right)=0 then DFSR(x)

Procedure DFSR(x)

  1. c:=c+1
  2. Dnum(x):=c
  3. for y\in V(x) do
  4. if Dnum(y)=0 then DFSR(y)

Построение каркаса

Следующий вариант алгоритма поиска в глубину отличается тем, что не использует стека для хранения открытых вершин. Стек нужен для того, чтобы в момент, когда окрестность активной вершины x исследована и необходимо сделать "шаг назад", можно было определить вершину, в которую нужно вернуться. Но это та вершина, которая является отцом вершины x в DFS-дереве. Поэтому, если решение задачи предусматривает построение DFS-дерева, то это дерево можно использовать и для организации "возвратных движений" в процессе обхода. Описываемый ниже алгоритм строит каркас произвольного графа, каждая компонента связности этого каркаса является DFS-деревом соответствующей компоненты связности графа. Через F(x) обозначается отец вершины x в этом DFS-дереве, при этом для корня дерева (стартовой вершины) a полагаем F(a)=a. Здесь и далее в описаниях алгоритмов инструкция "открыть (закрыть) вершину" означает, что вершина каким-то образом помечается как открытая (закрытая).

Алгоритм 2. Поиск в глубину с построением каркаса

  1. пометить все вершины как новые
  2. for a\in V do
  3. if вершина a новая then DFST(a)

Procedure DFST(a)

  1. F(a)\, :=a
  2. открыть вершину a
  3. x\, :=a
  4. while x открытая do
  5. if имеется неисследованное ребро (x,y)
  6. then исследовать ребро (x,y)
  7. if вершина y новая
  8. then F(y):=x
  9. открыть вершину y
  10. x:=y
  11. else закрыть вершину x
  12. x:=F(x)
< Лекция 4 || Лекция 5: 123 || Лекция 6 >
Петр Петров
Петр Петров

произведение графов К(2)*О(4) фактически 4 отдельных графа К(2)?

Александр Лаврентьев
Александр Лаврентьев

много инструкций вида if - then - else

Например Procedure DFS(a) опишите каким образом следует понимать вложенность инструкций. Как в языке С ? 

т.е. следующее 

if (...) then (...)

if (...) then (...)

else(...)

 

раскрывается как 

if (...) then (...)

if (...) then (...)

         else(...)

или так :

if (...) then

 {  (...)

     if (...) then (...)

              else(...)

}

обьясните пожалуйста.

 

 

Константин Дементьев
Константин Дементьев
Россия, г. Мичуринск