Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
лекция 16
Next
Download to read offline and view in fullscreen.

0

Share

Download to read offline

лекция 13

Download to read offline

Related Books

Free with a 30 day trial from Scribd

See all
  • Be the first to like this

лекция 13

  1. 1. Алгоритмы на графах и деревьях 1. Обходы в ширину и в глубину
  2. 2. При работе с графами часто приходится выполнять некоторое действие по одному разу с каждой из вершин графа. Например, некоторую порцию информации следует передать каждому из компьютеров в сети. При этом мы не хотим посещать какой-либо компьютер дважды. Аналогичная ситуация возникает, если мы хотим собрать информацию, а не распространить ее. Подобный обход можно совершать двумя различными способами. При обходе в глубину проход по выбранному пути осуществляется настолько глубоко, насколько это возможно, а при обходе в ширину (по уровням) мы равномерно двигаемся вдоль всех возможных направлений.
  3. 3. Поиск в ширину Пусть задан граф G = (V, Е) и выделена исходная вершина s. Алгоритм поиска в ширину систематически обходит все ребра G для «открытия» всех вершин, достижимых из s, вычисляя при этом расстояние (минимальное количество ребер) от s до каждой достижимой из s вершины. Кроме того, в процессе обхода строится «дерево поиска в ширину» с корнем s, содержащее все достижимые вершины. Для каждой достижимой из s вершины v путь в дереве поиска в ширину соответствует кратчайшему (т.е. содержащему наименьшее количество s ребер) пути от s до v в G. Алгоритм работает как для ориентированных, так и для неориентированных графов.
  4. 4. Поиск в ширину имеет такое название потому, что в процессе обхода мы идем вширь, т.е. перед тем как приступить к поиску вершин на расстоянии k +1, выполняется обход всех вершин на расстоянии k. Для отслеживания работы алгоритма поиск в ширину раскрашивает вершины графа в белый, серый и черный цвета. Изначально все вершины белые, и позже они могут стать серыми, а затем черными. Когда вершина открывается в процессе поиска, она окрашивается. Таким образом, серые и черные вершины – это вершины, которые уже были открыты, но алгоритм поиска в ширину поразному работает с ними, чтобы обеспечить объявленный порядок обхода. Если (u, v) ∈ Е и вершина u черного цвета, то вершина v либо серая, либо черная, т.е. все вершины, смежные с черной, уже открыты. Серые вершины могут иметь белых соседей, представляя собой границу между открытыми и неоткрытыми вершинами.
  5. 5. Поиск в ширину строит дерево поиска в ширину, которое изначально состоит из одного корня, которым является исходная вершина s. Если в процессе сканирования списка смежности уже открытой вершины u открывается белая вершина v, то вершина v и ребро (u, v) добавляются в дерево. Мы говорим, что u является предшественником, или родителем, v в дереве поиска вширь. Поскольку вершина может быть открыта не более одного раза, она имеет не более одного родителя. Взаимоотношения предков и потомков определяются в дереве поиска в ширину как обычно – если и находится на пути от корня s к вершине u, то u является предком v, а u –потомком u.
  6. 6. Процедура поиска в ширину BFS предполагает, что входной граф G = (V, Е) представлен при помощи списков смежности. Кроме того, поддерживаются дополнительные структуры данных в каждой вершине графа. Цвет каждой вершины u ∈ V хранится в переменной color[u], а предшественник – в переменной π[u]. Если предшественника у u нет (например, если u = s или u не открыта), то π[u] = NIL. Расстояние от s до вершины u, вычисляемое алгоритмом, хранится в поле d[u]. Алгоритм использует очередь Q для работы с множеством серых вершин:
  7. 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. 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. 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. 10. Выполним анализ времени работы алгоритма для входного графа G = (V,E). Сумма длин всех списков смежности равна O(|Е|), общее время, необходимое для сканирования списков, равно O(|Е|). Накладные расходы на инициализацию равны O(|V|), так что общее время работы процедуры BFS составляет O(|V| + |Е|). Таким образом, время поиска в ширину линейно зависит от размера представления графа G с использованием списков смежности. Очередь – это динамическое множество, элементы из которого удаляются согласно стратегии «первым вошел – первым вышел» (first-in, firstout – FIFO).
  11. 11. Очередь имеет голову (head) и хвост (tail). Очередь Q пуста, если выполняется условие head[Q] = tail[Q]. Изначально выполняется соотношение head[Q] = tail[Q] = 1. Если head[Q] = tail[Q] + l, то очередь заполнена, и попытка добавить в нее элемент приводит к ее переполнению. Рассмотрим процедуры добавления элемента в очередь Enqueue и удаления элемента из очереди Dequeue (в них проверка ошибок опустошения и переполнения не производится).
  12. 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. 13. Поиск в глубину Стратегия поиска в глубину, как следует из ее названия, состоит в том, чтобы идти «вглубь» графа, насколько это возможно. Когда вершина v открывается в процессе сканирования списка смежности уже открытой вершины u, процедура поиска записывает это событие, устанавливая поле предшественника v π[v] равным u. В отличие от поиска в ширину, где подграф предшествования образует дерево, при поиске в глубину подграф предшествования может состоять из нескольких деревьев, так как поиск может выполняться из нескольких исходных вершин.
  14. 14. Подграф предшествования поиска в глубину определяем как граф Gπ = (V, Еπ), где Еπ = {( π[v], v) : v ∈ V и π[v] ≠ NIL} . Подграф предшествования поиска в глубину образует лес поиска в глубину, который состоит из нескольких деревьев поиска в глубину. Ребра в Еπ называются ребрами дерева. Каждая вершина изначально белая, затем при открытии в процессе поиска она окрашивается в серый цвет, и по завершении, когда ее список смежности полностью сканирован, она становится черной. Такая методика гарантирует, что каждая вершина в конечном счете находится только в одном дереве поиска в глубину, так что деревья не пересекаются.
  15. 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. 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. 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. 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. 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. 20. общая стоимость выполнения строк 4 – 7 процедуры DFS_Visit равна Θ(|Е|). Время работы процедуры DFS, таким образом, равно Θ(|V| + | Е|). Процедура DFS_Visit является рекурсивной. Стек – это динамическое множество, элементы которого обрабатываются согласно стратегии «последним вошел – первым вышел» (last-in, first-out – LIFO). Чаще всего стек реализуется с помощью массива. Над стеком определены две основные операции – вставки и удаления элемента. Операция вставки применительно к стекам часто называется Push (запись в стек), а операция удаления – Pop (снятие со стека).
  21. 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

Views

Total views

228

On Slideshare

0

From embeds

0

Number of embeds

9

Actions

Downloads

2

Shares

0

Comments

0

Likes

0

×