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

Поиск в ширину

< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Аннотация: Поиск в ширину. Процедура поиска в ширину. BFS-дерево и вычисление расстояний.

Поиск в ширину

С этой лекции мы начинаем рассматривать алгоритмы для решения различных задач на графах. Сначала речь пойдет о задачах анализа графов с целью выявления их структуры и вычисления метрических характеристик. Многие задачи такого рода могут быть решены путем систематического обхода графа с посещением всех его вершин и исследованием всех его ребер. Такой обход можно выполнить многими способами, в действительности же широкое распространение благодаря своей простоте, а в большей степени своей полезности, получили две стратегии - поиск в глубину и поиск в ширину. Рассмотрение этих алгоритмов и примеров их применения к задачам анализа графов составляет содержание этой и двух следующих лекций.

Процедура поиска в ширину

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

Идея поиска в ширину состоит в том, чтобы посещать вершины в порядке их удаленности от некоторой заранее выбранной или указанной стартовой вершины a. Иначе говоря, сначала посещается сама вершина a, затем все вершины, смежные с a, то есть находящиеся от нее на расстоянии 1, затем вершины, находящиеся от a на расстоянии 2, и т.д.

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

Основная особенность поиска в ширину, отличающая его от других способов обхода графов, состоит в том, что в качестве активной вершины выбирается та из открытых, которая была посещена раньше других. Именно этим обеспечивается главное свойство поиска в ширину: чем ближе вершина к старту, тем раньше она будет посещена. Для реализации такого правила выбора активной вершины удобно использовать для хранения множества открытых вершин очередь - когда новая вершина становится открытой, она добавляется в конец очереди, а активная выбирается в ее начале. Схематически процесс изменения статуса вершин изображен на рис. 4.1. Черным кружком обозначена активная вершина.


Рис. 4.1.

Опишем процедуру поиска в ширину (BFS - от английского названия этого алгоритма - Breadth First Search) из заданной стартовой вершины a. В этом описании V(x) обозначает множество всех вершин, смежных с вершиной x, Q - очередь открытых вершин. Предполагается, что при посещении вершины она помечается как посещенная и эта пометка означает, что вершина уже не является новой.

Procedure BFS(a)

  1. посетить вершину a
  2. a\Rightarrow Q
  3. while Q\ne
\varnothing do
  4. x\Leftarrow Q
  5. for y\in V(x) do
  6. исследовать ребро (x,y)
  7. if вершина y новая
  8. then посетить вершину y
  9. y\Rightarrow Q

Отметим некоторые свойства процедуры BFS.

  1. Процедура BFS заканчивает работу после конечного числа шагов.

    Действительно, при каждом повторении цикла while из очереди удаляется одна вершина. Вершина добавляется к очереди только тогда, когда она посещается. Каждая вершина может быть посещена не более одного раза, так как посещаются только новые вершины, а в результате посещения вершина перестает быть новой. Таким образом, число повторений цикла while не превосходит числа вершин.

  2. В результате выполнения процедуры BFS будут посещены все вершины из компоненты связности, содержащей вершину a, и только они.

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

  3. Время работы процедуры BFS есть O(m), где m - число ребер в компоненте связности, содержащей вершину a.

Из предыдущих рассуждений видно, что каждая вершина из этой компоненты становится активной точно один раз. Внутренний цикл for для активной вершины x выполняется \deg (x) раз. Следовательно, общее число повторений внутреннего цикла будет равно \suml_{x\in VG}\deg (x) =2m.

Итак, процедура BFS( a ) производит обход компоненты связности, содержащей вершину a. Чтобы перейти к другой компоненте, достаточно выбрать какую-нибудь новую вершину (если такие вершины еще имеются), в качестве стартовой. Пусть V - множество вершин графа. Следующий алгоритм осуществляет полный обход графа методом поиска в ширину.

Алгоритм 1. Поиск в ширину.

  1. пометить все вершины как новые
  2. создать пустую очередь Q
  3. for a\in V do if a новая then BFS( a )

Учитывая, что цикл for в строке 3 повторяется n раз, где n - число вершин графа, получаем общую оценку трудоемкости O(m+n). Необходимо отметить, что эта оценка справедлива в предположении, что время, требуемое для просмотра окрестности вершины, пропорционально степени этой вершины. Это имеет место, например, если граф задан списками смежности. Если же граф задан матрицей смежности, то для просмотра окрестности любой вершины будет затрачиваться время, пропорциональное n. В этом случае общее время работы алгоритма будет оцениваться как O(n^{2}). Наибольшее значение величины m при данном n равно \frac{n(n-1)}{2}, т.е. имеет порядок n^{2}. Таким образом, трудоемкость алгоритма поиска в ширину при задании графа списками смежности не выше, чем при задании матрицей смежности. В целом же первый способ задания предпочтительнее, так как дает выигрыш для графов с небольшим числом ребер.

В качестве простейшего примера применения поиска в ширину для графа рассмотрим задачу выявления компонент связности. Допустим, мы хотим получить ответ в виде таблицы, в которой для каждой вершины x указан номер \comp(x) компоненты, которой принадлежит эта вершина. Компоненты будут получать номера в процессе обхода. Для решения этой задачи достаточно ввести переменную c со значением, равным текущему номеру компоненты, и каждый раз при посещении новой вершины x полагать \comp(x)=c. Значение c первоначально устанавливается равным 0 и модифицируется при каждом вызове процедуры BFS.

< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Петр Петров
Петр Петров

произведение графов К(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(...)

}

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

 

 

Дмитрий Крюков
Дмитрий Крюков
Россия, Москва
Андрей Посохов
Андрей Посохов
Россия, Санкт-Петербург