SlideShare una empresa de Scribd logo
1 de 21
Алгоритмы на графах и
деревьях

1. Обходы в ширину и в
глубину
При работе с графами часто приходится
выполнять некоторое действие по одному разу
с каждой из вершин графа. Например,
некоторую порцию информации следует
передать каждому из компьютеров в сети. При
этом мы не хотим посещать какой-либо
компьютер дважды. Аналогичная ситуация
возникает, если мы хотим собрать
информацию, а не распространить ее.
Подобный обход можно совершать двумя
различными способами. При обходе в глубину
проход по выбранному пути осуществляется
настолько глубоко, насколько это возможно, а
при обходе в ширину (по уровням) мы
равномерно двигаемся вдоль всех возможных
направлений.
Поиск в ширину
Пусть задан граф G = (V, Е) и выделена исходная
вершина s. Алгоритм поиска в ширину
систематически обходит все ребра G для
«открытия» всех вершин, достижимых из s,
вычисляя при этом расстояние (минимальное
количество ребер) от s до каждой достижимой из
s вершины. Кроме того, в процессе обхода
строится «дерево поиска в ширину» с корнем s,
содержащее все достижимые вершины. Для
каждой достижимой из s вершины v путь в
дереве поиска в ширину соответствует
кратчайшему (т.е. содержащему наименьшее
количество s ребер) пути от s до v в G. Алгоритм
работает как для ориентированных, так и для
неориентированных графов.
Поиск в ширину имеет такое название потому, что в
процессе обхода мы идем вширь, т.е. перед тем как
приступить к поиску вершин на расстоянии k +1,
выполняется обход всех вершин на расстоянии k.
Для отслеживания работы алгоритма поиск в ширину
раскрашивает вершины графа в белый, серый и
черный цвета.
Изначально все вершины белые, и позже они могут стать
серыми, а затем черными. Когда вершина открывается
в процессе поиска, она окрашивается. Таким образом,
серые и черные вершины – это вершины, которые уже
были открыты, но алгоритм поиска в ширину поразному работает с ними, чтобы обеспечить
объявленный порядок обхода. Если (u, v) ∈ Е и
вершина u черного цвета, то вершина v либо серая,
либо черная, т.е. все вершины, смежные с черной, уже
открыты. Серые вершины могут иметь белых соседей,
представляя собой границу между открытыми и
неоткрытыми вершинами.
Поиск в ширину строит дерево поиска в ширину,
которое изначально состоит из одного корня,
которым является исходная вершина s. Если в
процессе сканирования списка смежности уже
открытой вершины u открывается белая
вершина v, то вершина v и ребро (u, v)
добавляются в дерево. Мы говорим, что u
является предшественником, или родителем, v в
дереве поиска вширь. Поскольку вершина может
быть открыта не более одного раза, она имеет
не более одного родителя. Взаимоотношения
предков и потомков определяются в дереве
поиска в ширину как обычно – если и находится
на пути от корня s к вершине u, то u является
предком v, а u –потомком u.
Процедура поиска в ширину BFS
предполагает, что входной граф G = (V, Е)
представлен при помощи списков
смежности. Кроме того, поддерживаются
дополнительные структуры данных в
каждой вершине графа. Цвет каждой
вершины u ∈ V хранится в переменной
color[u], а предшественник – в переменной
π[u]. Если предшественника у u нет
(например, если u = s или u не открыта),
то π[u] = NIL. Расстояние от s до вершины
u, вычисляемое алгоритмом, хранится в
поле d[u]. Алгоритм использует очередь Q
для работы с множеством серых вершин:
BFS(G, s)
1. for (для) каждой вершины u ∈ V[G] – s
2. do color[u] ← WHITE (значение белого цвета)
3.
d[u] ← ∞
4.
π[u] ← NIL
5. color[s] ← GRAY (значение серого цвета)
6. d[s] ← 0
7. π[s] ← NIL
8. Q ← ∅
9. Enqueue(Q, s)
10. while Q ≠ ∅
11.
do u ← Dequeue(Q)
12.
for (для) каждой v ∈ Adj[u]
13.
do if color[v] = WHITE
14.
then color[v] ← GRAY
15.
d[v] ← d[v] + 1
16.
π[v] ← u
17.
Enqueue(Q, v)
18.
color[v] ← BLACK (значение черного цвета)
Процедура BFS работает следующим образом. В
строках 1 – 4 все вершины, за исключением
исходной вершины s, окрашиваются в белый
цвет, для каждой вершины u полю d[u]
присваивается значение ∞, а в качестве
родителя для каждой вершины устанавливается
NIL (пустое значение). В строке 5 исходная
вершина s окрашивается в серый цвет,
поскольку она рассматривается как открытая в
начале процедуры. В строке 6 ее полю d[s]
присваивается значение 0, а в строке 7 ее
родителем становится NIL. В строках 8 – 9
создается пустая очередь Q, в которую
помещается один элемент s.
Цикл while в строках 10 – 18 выполняется до тех
пор, пока остаются серые вершины (т.е.
открытые, но списки смежности которых еще не
просмотрены).
Инвариант данного цикла выглядит следующим образом:
При выполнении проверки в строке 10 очередь Q состоит
множества серых вершин.
Перед первой итерацией единственной серой вершиной и
единственной вершиной в очереди Q, является
исходная вершина s. В строке 11 определяется серая
вершина u в голове очереди Q, которая затем
удаляется из очереди. Цикл for в строках 12 – 17
просматривает все вершины v в списке смежности u.
Если вершина v белая, значит, она еще не открыта, и
алгоритм открывает ее, выполняя строки 14 – 17.
Вершине назначается серый цвет, дистанция d[v]
устанавливается равной d[u] + 1, а в качестве ее
родителя указывается вершина u. После этого
вершина помещается в хвост очереди Q. После того
как все вершины из списка смежности u просмотрены,
вершине u присваивается черный цвет. Инвариант
цикла сохраняется, так как все вершины, которые
окрашиваются в серый цвет (строка 14), вносятся в
очередь (строка 17), а вершина, которая удаляется из
очереди (строка 11), окрашивается в черный цвет
(строка 18).
Выполним анализ времени работы алгоритма для
входного графа
G = (V,E).
Сумма длин всех списков смежности равна O(|Е|),
общее время, необходимое для сканирования
списков, равно O(|Е|). Накладные расходы на
инициализацию равны O(|V|), так что общее
время работы процедуры BFS составляет O(|V| +
|Е|). Таким образом, время поиска в ширину
линейно зависит от размера представления
графа G с использованием списков смежности.
Очередь – это динамическое множество, элементы
из которого удаляются согласно стратегии
«первым вошел – первым вышел» (first-in, firstout – FIFO).
Очередь имеет голову (head) и хвост (tail).
Очередь Q пуста, если выполняется
условие head[Q] = tail[Q].
Изначально выполняется соотношение
head[Q] = tail[Q] = 1.
Если head[Q] = tail[Q] + l, то очередь
заполнена, и попытка добавить в нее
элемент приводит к ее переполнению.
Рассмотрим процедуры добавления
элемента в очередь Enqueue и удаления
элемента из очереди Dequeue (в них
проверка ошибок опустошения и
переполнения не производится).
Enqueue(Q, x)
1. Q[tail[Q]] ← x
2. if tail[Q] = length[Q]
3. then tail[Q] ← 1
4. else tail[Q] ← tail[Q] + 1
Dequeue(Q)
1. x ← Q[head[Q]]
2. if head[Q] = length[Q]
3. then head[Q] ← 1
4. else head[Q] ← head[Q] + 1
5. return x
Поиск в глубину
Стратегия поиска в глубину, как следует из ее
названия, состоит в том, чтобы идти «вглубь»
графа, насколько это возможно.
Когда вершина v открывается в процессе
сканирования списка смежности уже открытой
вершины u, процедура поиска записывает это
событие, устанавливая поле предшественника
v π[v] равным u. В отличие от поиска в ширину,
где подграф предшествования образует
дерево, при поиске в глубину подграф
предшествования может состоять из
нескольких деревьев, так как поиск может
выполняться из нескольких исходных вершин.
Подграф предшествования поиска в глубину
определяем как граф Gπ = (V, Еπ), где Еπ = {( π[v],
v) : v ∈ V и π[v] ≠ NIL} . Подграф
предшествования поиска в глубину образует лес
поиска в глубину, который состоит из нескольких
деревьев поиска в глубину. Ребра в Еπ
называются ребрами дерева.
Каждая вершина изначально белая, затем при
открытии в процессе поиска она окрашивается в
серый цвет, и по завершении, когда ее список
смежности полностью сканирован, она
становится черной. Такая методика гарантирует,
что каждая вершина в конечном счете находится
только в одном дереве поиска в глубину, так что
деревья не пересекаются.
Помимо построения леса поиск в глубину также
проставляет в вершинах метки времени. Каждая
вершина имеет две такие метки – первую d[v], в
которой указывается, когда вершина v открывается (и
окрашивается в серый цвет), и вторая – f[v], которая
фиксирует момент, когда поиск завершает
сканирование списка смежности вершины v и она
становится черной.
Процедура DFS записывает в поле d[u] момент, когда
вершина u открывается, а в поле f[u] – момент
завершения работы с вершиной u. Эти метки времени
представляют собой целые числа в диапазоне от 1 до
2|V|, поскольку для каждой из |V| вершин имеется
только одно событие открытия и одно – завершения.
Для каждой вершины u d[u] < f[u].
До момента времени d[u] вершина имеет цвет WHITE,
между d[u] и f[u] – цвет GRAY, а после f[u] – цвет
BLACK.
Псевдокод алгоритма поиска в глубину. Входной
граф G может быть как ориентированным, так и
неориентированным. Переменная time –
глобальная и используется нами для меток
времени.
DFS(G)
1. for (для) каждой вершины u ∈ V[G)
2.
do color[u] ← WHITE
3.
π[u] ← NIL
4. time ← 0
5. for (для) каждой вершины u ∈ V[G)
6.
do if color[u] = WHITE
7.
then DFS_Visit(u)
DFS_Visit(u)
1. соlоr[u] ← GRAY //Открыта белая вершина u
2. time ← time + 1
3. d[u] ← time
4. for (для) каждой вершины v ∈ Adj[u]
//Исследование ребра (u, v)
5.
do if color[v] = WHITE
6.
then π[v] ← u
7.
DFS_Visit(v)
8. color[u] ← BLACK //Завершение
9. f[u] ← time ← time + 1
Процедура DFS работает следующим образом. В
строках 1 – 3 все вершины окрашиваются в
белый цвет, а их поля π инициализируются
значением NIL. В строке 4 выполняется сброс
глобального счетчика времени.
В строках 5 – 7 поочередно проверяются все вершины из V,
и когда обнаруживается белая вершина, она
обрабатывается при помощи процедуры DFS_Visit.
Каждый раз при вызове процедуры DFS_Visit(u) в строке
7, вершина u становится корнем нового дерева леса
поиска в глубину. При возврате из процедуры DFS
каждой вершине u сопоставляются два момента времени
– время открытия d[u] и время завершения f[u].
При каждом вызове DFS_Visit(u) вершина u изначально
имеет белый цвет. В строке 1 она окрашивается в серый
цвет, в строке 2 увеличивается глобальная переменная
time, а в строке 3 выполняется запись нового значения
переменной time в поле времени открытия d[u]. В строках
4 – 7 исследуются все вершины, смежные с u, и
выполняется рекурсивное посещение белых вершин.
При рассмотрении в строке 4 вершины v ∈ Adj[u], мы
говорим, что ребро (u, v) исследуется поиском в глубину.
И, наконец, после того как будут исследованы все ребра,
покидающие u, в строках 8 – 9 вершина u окрашивается
в черный цвет, а в поле f[u] записывается время
завершения работы с ней.
Определим время работы процедуры DFS.
Циклы в строках 1 – 3 и
5–7
процедуры DFS выполняются за время Θ(|
V|), исключая время, необходимое для
вызова процедуры DFS_Visit. Процедура
DFS_Visit вызывается ровно по одному
разу для каждой вершины v ∈ V, так как
она вызывается только для белых вершин,
и первое, что она делает, – это
окрашивает переданную в качестве
параметра вершину в серый цвет. В
процессе выполнения DFS_Visit(v) цикл в
строках 4 – 7 выполняется |Adj[v]| раз.
Поскольку
∑| Adj[v] | = Θ(| E |)
v∈V
общая стоимость выполнения строк 4 – 7
процедуры DFS_Visit равна Θ(|Е|). Время работы
процедуры DFS, таким образом, равно Θ(|V| + |
Е|).
Процедура DFS_Visit является рекурсивной.
Стек – это динамическое множество, элементы
которого обрабатываются согласно стратегии
«последним вошел – первым вышел» (last-in,
first-out – LIFO). Чаще всего стек реализуется с
помощью массива. Над стеком определены две
основные операции – вставки и удаления
элемента. Операция вставки применительно к
стекам часто называется Push (запись в стек), а
операция удаления – Pop (снятие со стека).
Стек, способный вместить не более n элементов,
можно реализовать с помощью массива S[1..n].
Этот массив обладает атрибутом top[S],
представляющим собой индекс последнего
помещенного в стек элемента. Стек состоит из
элементов S [1.. top[S]], где S[1] – элемент на
дне стека, а
S[top[S]] – элемент на его
вершине.
Если top[S] = 0, то стек не содержит ни одного
элемента и является пустым. Протестировать
стек на наличие в нем элементов можно с
помощью операции-запроса Stack_Empty.
Stack_Empty(E)
1. if top[S] = 0
2. then return TRUE

Más contenido relacionado

La actualidad más candente

О-символика
О-символикаО-символика
О-символикаDEVTYPE
 
формула бине
формула бинеформула бине
формула бинеLeto24
 
николаева первообр интеграл
николаева первообр интегралниколаева первообр интеграл
николаева первообр интегралurvlan
 
Лекция 9: Графы. Поиск кратчайшего пути в графе
Лекция 9: Графы. Поиск кратчайшего пути в графеЛекция 9: Графы. Поиск кратчайшего пути в графе
Лекция 9: Графы. Поиск кратчайшего пути в графеMikhail Kurnosov
 
Динамика и статика — метрики графов социальных сетей - Cергей Зефиров
Динамика и статика — метрики графов социальных сетей - Cергей ЗефировДинамика и статика — метрики графов социальных сетей - Cергей Зефиров
Динамика и статика — метрики графов социальных сетей - Cергей ЗефировYandex
 
Hf labs education day. rocket science
Hf labs education day. rocket scienceHf labs education day. rocket science
Hf labs education day. rocket scienceOlga Kiseleva
 
зависимость между песочной группой графа и его матроидом
зависимость между песочной группой графа и его матроидомзависимость между песочной группой графа и его матроидом
зависимость между песочной группой графа и его матроидомИван Иванов
 
JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...
JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...
JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...JSFestUA
 
Ob pr simv
Ob pr simvOb pr simv
Ob pr simvMou Sk
 
Григорий Анатольевич Кабатянский - Конечные алгебры, геометрии и коды
Григорий Анатольевич Кабатянский - Конечные алгебры, геометрии и кодыГригорий Анатольевич Кабатянский - Конечные алгебры, геометрии и коды
Григорий Анатольевич Кабатянский - Конечные алгебры, геометрии и кодыYandex
 
Лекция 9. Поиск кратчайшего пути в графе
Лекция 9. Поиск кратчайшего пути в графеЛекция 9. Поиск кратчайшего пути в графе
Лекция 9. Поиск кратчайшего пути в графеMikhail Kurnosov
 
Ploshhad mnogougolnika
Ploshhad mnogougolnikaPloshhad mnogougolnika
Ploshhad mnogougolnikaDimon4
 
Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"
Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"
Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"Nikolay Grebenshikov
 
C++ осень 2012 лекция 11
C++ осень 2012 лекция 11C++ осень 2012 лекция 11
C++ осень 2012 лекция 11Technopark
 

La actualidad más candente (15)

О-символика
О-символикаО-символика
О-символика
 
формула бине
формула бинеформула бине
формула бине
 
николаева первообр интеграл
николаева первообр интегралниколаева первообр интеграл
николаева первообр интеграл
 
Лекция 9: Графы. Поиск кратчайшего пути в графе
Лекция 9: Графы. Поиск кратчайшего пути в графеЛекция 9: Графы. Поиск кратчайшего пути в графе
Лекция 9: Графы. Поиск кратчайшего пути в графе
 
Динамика и статика — метрики графов социальных сетей - Cергей Зефиров
Динамика и статика — метрики графов социальных сетей - Cергей ЗефировДинамика и статика — метрики графов социальных сетей - Cергей Зефиров
Динамика и статика — метрики графов социальных сетей - Cергей Зефиров
 
String
StringString
String
 
Hf labs education day. rocket science
Hf labs education day. rocket scienceHf labs education day. rocket science
Hf labs education day. rocket science
 
зависимость между песочной группой графа и его матроидом
зависимость между песочной группой графа и его матроидомзависимость между песочной группой графа и его матроидом
зависимость между песочной группой графа и его матроидом
 
JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...
JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...
JS Fest 2019/Autumn. Adam Leos. So why do you need to know Algorithms and Dat...
 
Ob pr simv
Ob pr simvOb pr simv
Ob pr simv
 
Григорий Анатольевич Кабатянский - Конечные алгебры, геометрии и коды
Григорий Анатольевич Кабатянский - Конечные алгебры, геометрии и кодыГригорий Анатольевич Кабатянский - Конечные алгебры, геометрии и коды
Григорий Анатольевич Кабатянский - Конечные алгебры, геометрии и коды
 
Лекция 9. Поиск кратчайшего пути в графе
Лекция 9. Поиск кратчайшего пути в графеЛекция 9. Поиск кратчайшего пути в графе
Лекция 9. Поиск кратчайшего пути в графе
 
Ploshhad mnogougolnika
Ploshhad mnogougolnikaPloshhad mnogougolnika
Ploshhad mnogougolnika
 
Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"
Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"
Лекция №16. Поиск подстрок. Предмет "Структуры и алгоритмы обработки данных"
 
C++ осень 2012 лекция 11
C++ осень 2012 лекция 11C++ осень 2012 лекция 11
C++ осень 2012 лекция 11
 

Destacado

слайды к лекции №13
слайды к лекции №13слайды к лекции №13
слайды к лекции №13student_kai
 
презентация писэх лабы
презентация писэх лабыпрезентация писэх лабы
презентация писэх лабыstudent_kai
 
презентация л.р. №14
презентация л.р. №14презентация л.р. №14
презентация л.р. №14student_kai
 
лаб. работа №3
лаб. работа №3лаб. работа №3
лаб. работа №3student_kai
 
кин лекция 8
кин лекция 8кин лекция 8
кин лекция 8student_kai
 
физика горения15
физика горения15физика горения15
физика горения15student_kai
 
лабораторная работа 4
лабораторная работа 4лабораторная работа 4
лабораторная работа 4student_kai
 
кин лекция 13
кин лекция 13кин лекция 13
кин лекция 13student_kai
 
презентация лекции №22
презентация лекции №22презентация лекции №22
презентация лекции №22student_kai
 

Destacado (20)

лекция 2
лекция 2лекция 2
лекция 2
 
лекция 16
лекция 16лекция 16
лекция 16
 
слайды к лекции №13
слайды к лекции №13слайды к лекции №13
слайды к лекции №13
 
презентация писэх лабы
презентация писэх лабыпрезентация писэх лабы
презентация писэх лабы
 
Prezentats lek
Prezentats lekPrezentats lek
Prezentats lek
 
презентация л.р. №14
презентация л.р. №14презентация л.р. №14
презентация л.р. №14
 
лаб. работа №3
лаб. работа №3лаб. работа №3
лаб. работа №3
 
кин лекция 8
кин лекция 8кин лекция 8
кин лекция 8
 
физика горения15
физика горения15физика горения15
физика горения15
 
лекция 6
лекция 6лекция 6
лекция 6
 
лабораторная работа 4
лабораторная работа 4лабораторная работа 4
лабораторная работа 4
 
лекция6
лекция6лекция6
лекция6
 
л14
л14л14
л14
 
1
11
1
 
кин лекция 13
кин лекция 13кин лекция 13
кин лекция 13
 
л 17 sld
л 17  sldл 17  sld
л 17 sld
 
лекция 17
лекция 17лекция 17
лекция 17
 
презентация лекции №22
презентация лекции №22презентация лекции №22
презентация лекции №22
 
лекция 10
лекция 10лекция 10
лекция 10
 
лекция 13
лекция 13лекция 13
лекция 13
 

Más de student_kai

презентация
презентацияпрезентация
презентацияstudent_kai
 
презентации продолжение банкета
презентации продолжение банкетапрезентации продолжение банкета
презентации продолжение банкетаstudent_kai
 
основы программирования на языке C
основы программирования на языке Cосновы программирования на языке C
основы программирования на языке Cstudent_kai
 
презентация курсовой работы
презентация курсовой работыпрезентация курсовой работы
презентация курсовой работыstudent_kai
 
лекция№34
лекция№34лекция№34
лекция№34student_kai
 
лекция№32
лекция№32лекция№32
лекция№32student_kai
 
лекция№33
лекция№33лекция№33
лекция№33student_kai
 
лекция№31
лекция№31лекция№31
лекция№31student_kai
 
лекция№30
лекция№30лекция№30
лекция№30student_kai
 
лекция№29
лекция№29лекция№29
лекция№29student_kai
 
лекция№28
лекция№28лекция№28
лекция№28student_kai
 
лекция№27
лекция№27лекция№27
лекция№27student_kai
 
лекция№26
лекция№26лекция№26
лекция№26student_kai
 
лекция№25
лекция№25лекция№25
лекция№25student_kai
 
лекция№25
лекция№25лекция№25
лекция№25student_kai
 
лекция№24
лекция№24лекция№24
лекция№24student_kai
 
лекция№23
лекция№23лекция№23
лекция№23student_kai
 
лекция№22
лекция№22лекция№22
лекция№22student_kai
 
лекция№21
лекция№21лекция№21
лекция№21student_kai
 
лекция№20
лекция№20лекция№20
лекция№20student_kai
 

Más de student_kai (20)

презентация
презентацияпрезентация
презентация
 
презентации продолжение банкета
презентации продолжение банкетапрезентации продолжение банкета
презентации продолжение банкета
 
основы программирования на языке C
основы программирования на языке Cосновы программирования на языке C
основы программирования на языке C
 
презентация курсовой работы
презентация курсовой работыпрезентация курсовой работы
презентация курсовой работы
 
лекция№34
лекция№34лекция№34
лекция№34
 
лекция№32
лекция№32лекция№32
лекция№32
 
лекция№33
лекция№33лекция№33
лекция№33
 
лекция№31
лекция№31лекция№31
лекция№31
 
лекция№30
лекция№30лекция№30
лекция№30
 
лекция№29
лекция№29лекция№29
лекция№29
 
лекция№28
лекция№28лекция№28
лекция№28
 
лекция№27
лекция№27лекция№27
лекция№27
 
лекция№26
лекция№26лекция№26
лекция№26
 
лекция№25
лекция№25лекция№25
лекция№25
 
лекция№25
лекция№25лекция№25
лекция№25
 
лекция№24
лекция№24лекция№24
лекция№24
 
лекция№23
лекция№23лекция№23
лекция№23
 
лекция№22
лекция№22лекция№22
лекция№22
 
лекция№21
лекция№21лекция№21
лекция№21
 
лекция№20
лекция№20лекция№20
лекция№20
 

лекция 13

  • 1. Алгоритмы на графах и деревьях 1. Обходы в ширину и в глубину
  • 2. При работе с графами часто приходится выполнять некоторое действие по одному разу с каждой из вершин графа. Например, некоторую порцию информации следует передать каждому из компьютеров в сети. При этом мы не хотим посещать какой-либо компьютер дважды. Аналогичная ситуация возникает, если мы хотим собрать информацию, а не распространить ее. Подобный обход можно совершать двумя различными способами. При обходе в глубину проход по выбранному пути осуществляется настолько глубоко, насколько это возможно, а при обходе в ширину (по уровням) мы равномерно двигаемся вдоль всех возможных направлений.
  • 3. Поиск в ширину Пусть задан граф G = (V, Е) и выделена исходная вершина s. Алгоритм поиска в ширину систематически обходит все ребра G для «открытия» всех вершин, достижимых из s, вычисляя при этом расстояние (минимальное количество ребер) от s до каждой достижимой из s вершины. Кроме того, в процессе обхода строится «дерево поиска в ширину» с корнем s, содержащее все достижимые вершины. Для каждой достижимой из s вершины v путь в дереве поиска в ширину соответствует кратчайшему (т.е. содержащему наименьшее количество s ребер) пути от s до v в G. Алгоритм работает как для ориентированных, так и для неориентированных графов.
  • 4. Поиск в ширину имеет такое название потому, что в процессе обхода мы идем вширь, т.е. перед тем как приступить к поиску вершин на расстоянии k +1, выполняется обход всех вершин на расстоянии k. Для отслеживания работы алгоритма поиск в ширину раскрашивает вершины графа в белый, серый и черный цвета. Изначально все вершины белые, и позже они могут стать серыми, а затем черными. Когда вершина открывается в процессе поиска, она окрашивается. Таким образом, серые и черные вершины – это вершины, которые уже были открыты, но алгоритм поиска в ширину поразному работает с ними, чтобы обеспечить объявленный порядок обхода. Если (u, v) ∈ Е и вершина u черного цвета, то вершина v либо серая, либо черная, т.е. все вершины, смежные с черной, уже открыты. Серые вершины могут иметь белых соседей, представляя собой границу между открытыми и неоткрытыми вершинами.
  • 5. Поиск в ширину строит дерево поиска в ширину, которое изначально состоит из одного корня, которым является исходная вершина s. Если в процессе сканирования списка смежности уже открытой вершины u открывается белая вершина v, то вершина v и ребро (u, v) добавляются в дерево. Мы говорим, что u является предшественником, или родителем, v в дереве поиска вширь. Поскольку вершина может быть открыта не более одного раза, она имеет не более одного родителя. Взаимоотношения предков и потомков определяются в дереве поиска в ширину как обычно – если и находится на пути от корня s к вершине u, то u является предком v, а u –потомком u.
  • 6. Процедура поиска в ширину BFS предполагает, что входной граф G = (V, Е) представлен при помощи списков смежности. Кроме того, поддерживаются дополнительные структуры данных в каждой вершине графа. Цвет каждой вершины u ∈ V хранится в переменной color[u], а предшественник – в переменной π[u]. Если предшественника у u нет (например, если u = s или u не открыта), то π[u] = NIL. Расстояние от s до вершины u, вычисляемое алгоритмом, хранится в поле d[u]. Алгоритм использует очередь Q для работы с множеством серых вершин:
  • 7. BFS(G, s) 1. for (для) каждой вершины u ∈ V[G] – s 2. do color[u] ← WHITE (значение белого цвета) 3. d[u] ← ∞ 4. π[u] ← NIL 5. color[s] ← GRAY (значение серого цвета) 6. d[s] ← 0 7. π[s] ← NIL 8. Q ← ∅ 9. Enqueue(Q, s) 10. while Q ≠ ∅ 11. do u ← Dequeue(Q) 12. for (для) каждой v ∈ Adj[u] 13. do if color[v] = WHITE 14. then color[v] ← GRAY 15. d[v] ← d[v] + 1 16. π[v] ← u 17. Enqueue(Q, v) 18. color[v] ← BLACK (значение черного цвета)
  • 8. Процедура BFS работает следующим образом. В строках 1 – 4 все вершины, за исключением исходной вершины s, окрашиваются в белый цвет, для каждой вершины u полю d[u] присваивается значение ∞, а в качестве родителя для каждой вершины устанавливается NIL (пустое значение). В строке 5 исходная вершина s окрашивается в серый цвет, поскольку она рассматривается как открытая в начале процедуры. В строке 6 ее полю d[s] присваивается значение 0, а в строке 7 ее родителем становится NIL. В строках 8 – 9 создается пустая очередь Q, в которую помещается один элемент s. Цикл while в строках 10 – 18 выполняется до тех пор, пока остаются серые вершины (т.е. открытые, но списки смежности которых еще не просмотрены).
  • 9. Инвариант данного цикла выглядит следующим образом: При выполнении проверки в строке 10 очередь Q состоит множества серых вершин. Перед первой итерацией единственной серой вершиной и единственной вершиной в очереди Q, является исходная вершина s. В строке 11 определяется серая вершина u в голове очереди Q, которая затем удаляется из очереди. Цикл for в строках 12 – 17 просматривает все вершины v в списке смежности u. Если вершина v белая, значит, она еще не открыта, и алгоритм открывает ее, выполняя строки 14 – 17. Вершине назначается серый цвет, дистанция d[v] устанавливается равной d[u] + 1, а в качестве ее родителя указывается вершина u. После этого вершина помещается в хвост очереди Q. После того как все вершины из списка смежности u просмотрены, вершине u присваивается черный цвет. Инвариант цикла сохраняется, так как все вершины, которые окрашиваются в серый цвет (строка 14), вносятся в очередь (строка 17), а вершина, которая удаляется из очереди (строка 11), окрашивается в черный цвет (строка 18).
  • 10. Выполним анализ времени работы алгоритма для входного графа G = (V,E). Сумма длин всех списков смежности равна O(|Е|), общее время, необходимое для сканирования списков, равно O(|Е|). Накладные расходы на инициализацию равны O(|V|), так что общее время работы процедуры BFS составляет O(|V| + |Е|). Таким образом, время поиска в ширину линейно зависит от размера представления графа G с использованием списков смежности. Очередь – это динамическое множество, элементы из которого удаляются согласно стратегии «первым вошел – первым вышел» (first-in, firstout – FIFO).
  • 11. Очередь имеет голову (head) и хвост (tail). Очередь Q пуста, если выполняется условие head[Q] = tail[Q]. Изначально выполняется соотношение head[Q] = tail[Q] = 1. Если head[Q] = tail[Q] + l, то очередь заполнена, и попытка добавить в нее элемент приводит к ее переполнению. Рассмотрим процедуры добавления элемента в очередь Enqueue и удаления элемента из очереди Dequeue (в них проверка ошибок опустошения и переполнения не производится).
  • 12. Enqueue(Q, x) 1. Q[tail[Q]] ← x 2. if tail[Q] = length[Q] 3. then tail[Q] ← 1 4. else tail[Q] ← tail[Q] + 1 Dequeue(Q) 1. x ← Q[head[Q]] 2. if head[Q] = length[Q] 3. then head[Q] ← 1 4. else head[Q] ← head[Q] + 1 5. return x
  • 13. Поиск в глубину Стратегия поиска в глубину, как следует из ее названия, состоит в том, чтобы идти «вглубь» графа, насколько это возможно. Когда вершина v открывается в процессе сканирования списка смежности уже открытой вершины u, процедура поиска записывает это событие, устанавливая поле предшественника v π[v] равным u. В отличие от поиска в ширину, где подграф предшествования образует дерево, при поиске в глубину подграф предшествования может состоять из нескольких деревьев, так как поиск может выполняться из нескольких исходных вершин.
  • 14. Подграф предшествования поиска в глубину определяем как граф Gπ = (V, Еπ), где Еπ = {( π[v], v) : v ∈ V и π[v] ≠ NIL} . Подграф предшествования поиска в глубину образует лес поиска в глубину, который состоит из нескольких деревьев поиска в глубину. Ребра в Еπ называются ребрами дерева. Каждая вершина изначально белая, затем при открытии в процессе поиска она окрашивается в серый цвет, и по завершении, когда ее список смежности полностью сканирован, она становится черной. Такая методика гарантирует, что каждая вершина в конечном счете находится только в одном дереве поиска в глубину, так что деревья не пересекаются.
  • 15. Помимо построения леса поиск в глубину также проставляет в вершинах метки времени. Каждая вершина имеет две такие метки – первую d[v], в которой указывается, когда вершина v открывается (и окрашивается в серый цвет), и вторая – f[v], которая фиксирует момент, когда поиск завершает сканирование списка смежности вершины v и она становится черной. Процедура DFS записывает в поле d[u] момент, когда вершина u открывается, а в поле f[u] – момент завершения работы с вершиной u. Эти метки времени представляют собой целые числа в диапазоне от 1 до 2|V|, поскольку для каждой из |V| вершин имеется только одно событие открытия и одно – завершения. Для каждой вершины u d[u] < f[u]. До момента времени d[u] вершина имеет цвет WHITE, между d[u] и f[u] – цвет GRAY, а после f[u] – цвет BLACK.
  • 16. Псевдокод алгоритма поиска в глубину. Входной граф G может быть как ориентированным, так и неориентированным. Переменная time – глобальная и используется нами для меток времени. DFS(G) 1. for (для) каждой вершины u ∈ V[G) 2. do color[u] ← WHITE 3. π[u] ← NIL 4. time ← 0 5. for (для) каждой вершины u ∈ V[G) 6. do if color[u] = WHITE 7. then DFS_Visit(u)
  • 17. DFS_Visit(u) 1. соlоr[u] ← GRAY //Открыта белая вершина u 2. time ← time + 1 3. d[u] ← time 4. for (для) каждой вершины v ∈ Adj[u] //Исследование ребра (u, v) 5. do if color[v] = WHITE 6. then π[v] ← u 7. DFS_Visit(v) 8. color[u] ← BLACK //Завершение 9. f[u] ← time ← time + 1 Процедура DFS работает следующим образом. В строках 1 – 3 все вершины окрашиваются в белый цвет, а их поля π инициализируются значением NIL. В строке 4 выполняется сброс глобального счетчика времени.
  • 18. В строках 5 – 7 поочередно проверяются все вершины из V, и когда обнаруживается белая вершина, она обрабатывается при помощи процедуры DFS_Visit. Каждый раз при вызове процедуры DFS_Visit(u) в строке 7, вершина u становится корнем нового дерева леса поиска в глубину. При возврате из процедуры DFS каждой вершине u сопоставляются два момента времени – время открытия d[u] и время завершения f[u]. При каждом вызове DFS_Visit(u) вершина u изначально имеет белый цвет. В строке 1 она окрашивается в серый цвет, в строке 2 увеличивается глобальная переменная time, а в строке 3 выполняется запись нового значения переменной time в поле времени открытия d[u]. В строках 4 – 7 исследуются все вершины, смежные с u, и выполняется рекурсивное посещение белых вершин. При рассмотрении в строке 4 вершины v ∈ Adj[u], мы говорим, что ребро (u, v) исследуется поиском в глубину. И, наконец, после того как будут исследованы все ребра, покидающие u, в строках 8 – 9 вершина u окрашивается в черный цвет, а в поле f[u] записывается время завершения работы с ней.
  • 19. Определим время работы процедуры DFS. Циклы в строках 1 – 3 и 5–7 процедуры DFS выполняются за время Θ(| V|), исключая время, необходимое для вызова процедуры DFS_Visit. Процедура DFS_Visit вызывается ровно по одному разу для каждой вершины v ∈ V, так как она вызывается только для белых вершин, и первое, что она делает, – это окрашивает переданную в качестве параметра вершину в серый цвет. В процессе выполнения DFS_Visit(v) цикл в строках 4 – 7 выполняется |Adj[v]| раз. Поскольку ∑| Adj[v] | = Θ(| E |) v∈V
  • 20. общая стоимость выполнения строк 4 – 7 процедуры DFS_Visit равна Θ(|Е|). Время работы процедуры DFS, таким образом, равно Θ(|V| + | Е|). Процедура DFS_Visit является рекурсивной. Стек – это динамическое множество, элементы которого обрабатываются согласно стратегии «последним вошел – первым вышел» (last-in, first-out – LIFO). Чаще всего стек реализуется с помощью массива. Над стеком определены две основные операции – вставки и удаления элемента. Операция вставки применительно к стекам часто называется Push (запись в стек), а операция удаления – Pop (снятие со стека).
  • 21. Стек, способный вместить не более n элементов, можно реализовать с помощью массива S[1..n]. Этот массив обладает атрибутом top[S], представляющим собой индекс последнего помещенного в стек элемента. Стек состоит из элементов S [1.. top[S]], где S[1] – элемент на дне стека, а S[top[S]] – элемент на его вершине. Если top[S] = 0, то стек не содержит ни одного элемента и является пустым. Протестировать стек на наличие в нем элементов можно с помощью операции-запроса Stack_Empty. Stack_Empty(E) 1. if top[S] = 0 2. then return TRUE