Обход в ширину — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(почти полностью переписал статью)
Строка 1: Строка 1:
 
{{В разработке}}
 
{{В разработке}}
'''Обход в ширину''' ('''Поиск в ширину, BFS''', Breadth-first search) — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами. Например, алгоритм [[Алгоритм Прима|Прима]] поиска минимального остовного дерева или алгоритм [[Алгоритм Дейкстры|Дейкстры]] поиска кратчайшего пути из одной вершины используют идеи, сходные идеям, используемым при поиске в ширину.
+
'''Обход в ширину''' ('''Поиск в ширину, BFS''', Breadth-first search) — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.
  
==Алгоритм==
+
== Алгоритм ==
  
===Общая идея===
+
=== Общая идея ===
Пусть задан граф <tex>G = (V, E)</tex> и выделена исходная вершина <tex>s</tex>. Алгоритм поиска в ширину систематически обходит все ребра <tex>G</tex> для "открытия" всех вершин, достижимых из <tex>s</tex>, вычисляя при этом расстояние (минимальное количество ребер) от <tex>s</tex> до каждой достижимой из <tex>s</tex> вершины. Кроме того, в процессе обхода строится "дерево поиска в ширину" с корнем <tex>s</tex>, содержащее все достижимые вершины. Для каждой достижимой их <tex>s</tex> вершины <tex>v</tex> путь в дереве поиска в ширину соответствует кратчайшему пути от <tex>s</tex> до <tex>v</tex> в <tex>G</tex>.
 
  
Для отслеживания работы алгоритма поиск в ширину раскрашивает вершины графа в белый, серый и черный цвета. Изначально все вершины белые, и позже они могут стать серыми, а затем черными. Когда вершина '''открывается''' в процессе поиска, она окрашивается. Таким образом, серые и черные вершины — это вершины, которые уже были открыты, но алгоритм поиска в ширину по-разному работает с ними, чтобы обеспечить объявленный порядок обхода. Если <tex>(u, v) \in E</tex> и вершина <tex>u</tex> черного цвета, то вершина <tex>v</tex> либо серая, либо черная, т.е. все вершины, смежные с ней уже открыты. Серые вершины могут иметь белых соседей, представляя собой границу между открытыми и неоткрытыми вершинами.
+
Пусть задан невзвешенный граф <tex>G = (V, E)</tex>, в котором выделена исходная вершина <tex>s</tex>. Для алгоритма нам потребуются очередь, которая сначала содержит только <tex> s </tex>, и множество посещенных вершин <tex> X </tex>, которое изначально тоже содержит только <tex> s </tex>. На каждом шаге алгоритм вынимает из начала очереди вершину, рассматривает все исходящие из нее ребра и добавляет все связанные с ней непосещенные вершины в <tex> X </tex> и в конец очереди. Если очередь пуста, то алгоритм завершает работу.
  
Поиск в ширину строит дерево поиска в ширину, которое изначально состоит из одного корня, которым является исходная вершина <tex>s</tex>. Если в процессе сканирования списка смежности уже открытой вершины <tex>u</tex> открывается белая вершина <tex>v</tex>, то вершина <tex>v</tex> и ребро <tex>(u, v)</tex> добавляются в дерево. При этом <tex>u</tex> является '''предшественником''' (predecessor), или '''родителем''' (parent), <tex>v</tex> в дереве поиска в ширину. Поскольку вершина может быть открыта не более одного раза, она имеет не более одного родителя.
+
Поиск в ширину также может построить дерево поиска в ширину. Изначально оно состоит из одного корня <tex> s </tex>. Когда мы добавляем непосещенную вершину в очередь, то добавляем ее и ребро, по которому мы до нее дошли, в дерево. Поскольку каждая вершина может быть посещена не более одного раза, она имеет не более одного родителя. После окончания работы алгоритма для каждой достижимой из <tex> s </tex> вершины <tex> t </tex> путь в дереве поиска в ширину соответствует кратчайшему пути от <tex> s </tex> до <tex> t </tex> в <tex> G </tex>.
 +
 
 +
Также можно для каждой вершины <tex> t \in V </tex> считать длину этого пути, равную <tex> d[t] </tex>. Можно считать, что для непосещенных вершин эта длина бесконечно велика. Тогда на каждом шаге длина пути до <tex> t </tex> равна <tex> \rho(s, t) </tex>, если <tex> t </tex> посещена и <tex> \infty </tex> в противном случае. Отсюда следует, что если на каждом шаге обновлять длины путей, то информация о множестве <tex> X </tex> является избыточной, и его можно не хранить.
 +
 
 +
== Анализ времени работы ==
 +
 
 +
Оценим время работы для входного графа <tex>G = (V, E)</tex>. В очередь добавляются только непосещенные вершины, поэтому каждая вершина посещается не более одного раза. Операции внесения в очередь и удаления из нее требуют <tex> O(1) </tex> времени, так что общее время работы с очередью составляет <tex> O(V) </tex> операций. Для каждой вершины <tex> v </tex> рассматривается не более <tex> deg\ v </tex> ребер, инцидентных ей. Так как <tex> \sum_{v \in V} deп\ v = 2|E| </tex>, то время, используемое на работу с ребрами, составляет <tex> O(E) </tex>. Поэтому общее время работы алгоритма поиска в ширину — <tex> O(V + E) </tex>.
 +
 
 +
=== Корректность ===
 +
 
 +
{{Утверждение
 +
|statement=
 +
В алгоритме поиска в ширину очередь всегда содержит сначала вершины с расстоянием k, а потом, возможно, вершины с расстоянием k + 1.
 +
|proof=
 +
Докажем это утверждение индукцией по шагам алгоритма.
 +
 
 +
База: изначально очередь содержит только одну вершину <tex> s </tex> с расстоянием 0, утверждение верно.
 +
 
 +
Переход: пусть после <tex> l </tex>-ого шага алгоритма очередь содержит <tex> p </tex> вершин с расстоянием <tex> k </tex> и <tex> q </tex> вершин с расстоянием <tex> k + 1 </tex>. Тогда на <tex> l+1 </tex>-ом шаге мы извлечем из очереди одну вершину и добавим в нее все непосещенные(<tex> r </tex> вершин), связанные с ней; расстояние до них, очевидно, будет равно <tex> k + 1 </tex>. У нас останется <tex> p - 1 </tex>(возможно, 0) вершин с расстоянием <tex> k </tex> и <tex> q + r </tex> вершин с расстоянием k + 1, что соответствует нашему инварианту.
 +
}}
 +
 
 +
{{Теорема
 +
|statement=
 +
Алгоритм поиска в ширину в невзвешенном графе находит оптимальные расстояния до всех достижимых вершин.
 +
|proof=
 +
Допустим, что это не так. Выберем из вершин, для которых найдено неоптимальное расстояние от <tex> s </tex> ту, которая находится на минимальном расстоянии. Пусть это вершина <tex> u </tex>, и она имеет своим предком в дереве обхода в ширину <tex> v </tex>, а предкок в оптимальном пути до <tex> v </tex> — вершина <tex> w </tex>. Расстояние до вершин <tex> v <tex> и <tex> w </tex> найдено корректно, путь через <tex> w </tex> оптимальный, а через <tex> v </tex> — нет, поэтому <tex> d[w] < d[v] </tex>. Из ранее доказанной леммы следует, что в этом случае вершина <tex> w </tex> попала в очередь и была обработана раньше, чем <tex> v </tex>. Но она соединена с <tex> u </tex>, значит, <tex> v </tex> не может быть предком <tex> u </tex> в дереве обхода в ширину, мы пришли к противоречию, и найденные расстояния до всех вершин оптимальны.
 +
}}
 +
 
 +
=== Реализация ===
 +
 
 +
В приведенном ниже псевдокоде <tex> G = (V, E) </tex> - входной граф, <tex> s </tex> - выделенная вершина, Q - очередь. Множество <tex> X </tex> не хранится, вместо него использются расстояния в дереве обхода в ширину; расстояние от <tex>s</tex> до вершины <tex>u</tex>, вычисляемое алгоритмом, хранится в поле <tex>d[u]</tex>.
  
=== Реализация===
 
Приведенная ниже процедура поиска в ширину '''BFS''' предполагает, что входной граф <tex>G = (V, E)</tex> представлен при помощи списков смежности. Кроме того, поддерживаются дополнительные структуры данных в каждой вершине графа. Цвет каждой вершины <tex>u \in V</tex> хранится в переменной <tex>color[u]</tex>, а предшественник — в переменной <tex>p[u]</tex>. Если предшественника у <tex>u</tex> нет, то <tex>p[u] =</tex> NIL. Расстояние от <tex>s</tex> до вершины <tex>u</tex>, вычисляемое алгоритмом, хранится в поле <tex>d[u]</tex>. Алгоритм использует очередь <tex>Q</tex> для работы с множеством серых вершин.
 
 
  '''BFS'''(<tex>G</tex>, <tex>s</tex>)
 
  '''BFS'''(<tex>G</tex>, <tex>s</tex>)
  1 '''for''' <tex>u \in V[G]</tex>
+
1    d[s] <tex> \leftarrow </tex> 0
  2   '''do'''  <tex>color[u] \leftarrow</tex> WHITE
+
  2   Q <tex> \leftarrow \varnothing </tex>
  3      <tex>d[u] \leftarrow \mathcal {1}</tex>
+
3   Q.push(s)
  4      <tex>p[u] \leftarrow</tex> NIL
+
4   '''while''' Q <tex> \ne \varnothing </tex>  
  5  <tex>color[s] \leftarrow</tex> GRAY
+
  5      '''do''' u <tex> \leftarrow </tex> Q.pop
  6  <tex>d[s] \leftarrow 0</tex>
+
6        '''for''' v: uv <tex> \in </tex> E
  7 <tex>p[s] \leftarrow</tex> NIL
+
7          '''do''' '''if''' d[v] = <tex> \infty </tex>
   <tex>Q \leftarrow \varnothing</tex>  
+
8            '''then''' d[v] <tex> \leftarrow </tex> d[u] + 1
   9  ENQUEUE(<tex>Q</tex>, <tex>s</tex>)
+
9                Q.push(v)
   10 '''while''' <tex>Q \ne \varnothing</tex>
 
  11 '''do''' <tex>u \leftarrow</tex> DEQUEUE(<tex>Q</tex>)
 
  12    '''for''' <tex>v \in Adj[u]</tex>
 
  13      '''do''' '''if''' <tex>color[v] =</tex> WHITE
 
  14        '''then''' <tex>color[v] \leftarrow</tex> GRAY
 
  15              <tex>d[v] \leftarrow d[u] + 1</tex>
 
  16              <tex>p[v] \leftarrow u</tex>
 
  17              ENQUEUE(<tex>Q</tex>, <tex>v</tex>)
 
  18    <tex>color[u] \leftarrow</tex>BLACK
 
  
== Анализ ==
+
== Литература ==
Оценим время работы для входного графа <tex>G = (V, E)</tex>. После инициализации ни одна вершина не окрашивается в белый цвет, поэтому проверка в строке 13 гарантирует, что каждая вершина вносится в очередь не более одного раза, а следовательно, и удаляется из очереди она не более одного раза. Операции внесения в очередь и удаления из нее требуют <tex>O(1)</tex> времени, так что общее время операций с очередью составляет <tex>O(V)</tex>. Поскольку каждый список смежности сканируется только при удалении соответствующей вершины из очереди, каждый список сканируется не более одного раза. Так как сумма длин всех списков смежности равна <tex> \Theta (E) </tex>, общее время, необходимое для сканирования списков, равно <tex>O(E)</tex>. Накладные расходы на инициализацию равны <tex>O(V)</tex>, так что общее время работы процедуры '''BFS''' составляет <tex>O(V + E)</tex>. Таким образом, время работы поиска в ширину линейно зависит от размера представления графа <tex>G</tex> с использованием списков смежности.
 
  
== Литература ==
 
 
*Томас Х. Кормен и др. Алгоритмы: построение и анализ = INTRODUCTION TO ALGORITHMS. — 2-е изд. — М.: «Вильямс», 2006. — С. 1296. — ISBN 0-07-013151-1
 
*Томас Х. Кормен и др. Алгоритмы: построение и анализ = INTRODUCTION TO ALGORITHMS. — 2-е изд. — М.: «Вильямс», 2006. — С. 1296. — ISBN 0-07-013151-1
 +
 +
== Ссылки ==
 +
 +
[http://e-maxx.ru/algo/bfs Поиск в ширину на e-maxx.ru]
 +
[http://ru.wikipedia.org/wiki/Поиск_в_ширину Поиск в ширину в Википедии]
 +
[http://rain.ifmo.ru/cat/view.php/vis/graph-general/bfs-2002 Визуализатор алгоритма]
 +
  
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Кратчайшие пути в графах]]
 
[[Категория: Кратчайшие пути в графах]]

Версия 04:40, 27 октября 2011

Эта статья находится в разработке!

Обход в ширину (Поиск в ширину, BFS, Breadth-first search) — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.

Алгоритм

Общая идея

Пусть задан невзвешенный граф [math]G = (V, E)[/math], в котором выделена исходная вершина [math]s[/math]. Для алгоритма нам потребуются очередь, которая сначала содержит только [math] s [/math], и множество посещенных вершин [math] X [/math], которое изначально тоже содержит только [math] s [/math]. На каждом шаге алгоритм вынимает из начала очереди вершину, рассматривает все исходящие из нее ребра и добавляет все связанные с ней непосещенные вершины в [math] X [/math] и в конец очереди. Если очередь пуста, то алгоритм завершает работу.

Поиск в ширину также может построить дерево поиска в ширину. Изначально оно состоит из одного корня [math] s [/math]. Когда мы добавляем непосещенную вершину в очередь, то добавляем ее и ребро, по которому мы до нее дошли, в дерево. Поскольку каждая вершина может быть посещена не более одного раза, она имеет не более одного родителя. После окончания работы алгоритма для каждой достижимой из [math] s [/math] вершины [math] t [/math] путь в дереве поиска в ширину соответствует кратчайшему пути от [math] s [/math] до [math] t [/math] в [math] G [/math].

Также можно для каждой вершины [math] t \in V [/math] считать длину этого пути, равную [math] d[t] [/math]. Можно считать, что для непосещенных вершин эта длина бесконечно велика. Тогда на каждом шаге длина пути до [math] t [/math] равна [math] \rho(s, t) [/math], если [math] t [/math] посещена и [math] \infty [/math] в противном случае. Отсюда следует, что если на каждом шаге обновлять длины путей, то информация о множестве [math] X [/math] является избыточной, и его можно не хранить.

Анализ времени работы

Оценим время работы для входного графа [math]G = (V, E)[/math]. В очередь добавляются только непосещенные вершины, поэтому каждая вершина посещается не более одного раза. Операции внесения в очередь и удаления из нее требуют [math] O(1) [/math] времени, так что общее время работы с очередью составляет [math] O(V) [/math] операций. Для каждой вершины [math] v [/math] рассматривается не более [math] deg\ v [/math] ребер, инцидентных ей. Так как [math] \sum_{v \in V} deп\ v = 2|E| [/math], то время, используемое на работу с ребрами, составляет [math] O(E) [/math]. Поэтому общее время работы алгоритма поиска в ширину — [math] O(V + E) [/math].

Корректность

Утверждение:
В алгоритме поиска в ширину очередь всегда содержит сначала вершины с расстоянием k, а потом, возможно, вершины с расстоянием k + 1.
[math]\triangleright[/math]

Докажем это утверждение индукцией по шагам алгоритма.

База: изначально очередь содержит только одну вершину [math] s [/math] с расстоянием 0, утверждение верно.

Переход: пусть после [math] l [/math]-ого шага алгоритма очередь содержит [math] p [/math] вершин с расстоянием [math] k [/math] и [math] q [/math] вершин с расстоянием [math] k + 1 [/math]. Тогда на [math] l+1 [/math]-ом шаге мы извлечем из очереди одну вершину и добавим в нее все непосещенные([math] r [/math] вершин), связанные с ней; расстояние до них, очевидно, будет равно [math] k + 1 [/math]. У нас останется [math] p - 1 [/math](возможно, 0) вершин с расстоянием [math] k [/math] и [math] q + r [/math] вершин с расстоянием k + 1, что соответствует нашему инварианту.
[math]\triangleleft[/math]
Теорема:
Алгоритм поиска в ширину в невзвешенном графе находит оптимальные расстояния до всех достижимых вершин.
Доказательство:
[math]\triangleright[/math]
Допустим, что это не так. Выберем из вершин, для которых найдено неоптимальное расстояние от [math] s [/math] ту, которая находится на минимальном расстоянии. Пусть это вершина [math] u [/math], и она имеет своим предком в дереве обхода в ширину [math] v [/math], а предкок в оптимальном пути до [math] v [/math] — вершина [math] w [/math]. Расстояние до вершин [math] v \lt tex\gt и \lt tex\gt w [/math] найдено корректно, путь через [math] w [/math] оптимальный, а через [math] v [/math] — нет, поэтому [math] d[w] \lt d[v] [/math]. Из ранее доказанной леммы следует, что в этом случае вершина [math] w [/math] попала в очередь и была обработана раньше, чем [math] v [/math]. Но она соединена с [math] u [/math], значит, [math] v [/math] не может быть предком [math] u [/math] в дереве обхода в ширину, мы пришли к противоречию, и найденные расстояния до всех вершин оптимальны.
[math]\triangleleft[/math]

Реализация

В приведенном ниже псевдокоде [math] G = (V, E) [/math] - входной граф, [math] s [/math] - выделенная вершина, Q - очередь. Множество [math] X [/math] не хранится, вместо него использются расстояния в дереве обхода в ширину; расстояние от [math]s[/math] до вершины [math]u[/math], вычисляемое алгоритмом, хранится в поле [math]d[u][/math].

BFS([math]G[/math], [math]s[/math])
1    d[s] [math] \leftarrow [/math] 0
2    Q [math] \leftarrow \varnothing [/math]
3    Q.push(s)
4    while Q [math] \ne \varnothing [/math] 
5      do u [math] \leftarrow [/math] Q.pop
6        for v: uv [math] \in [/math] E
7          do if d[v] = [math] \infty [/math]
8            then d[v] [math] \leftarrow [/math] d[u] + 1
9                 Q.push(v)

Литература

  • Томас Х. Кормен и др. Алгоритмы: построение и анализ = INTRODUCTION TO ALGORITHMS. — 2-е изд. — М.: «Вильямс», 2006. — С. 1296. — ISBN 0-07-013151-1

Ссылки

Поиск в ширину на e-maxx.ru Поиск в ширину в Википедии Визуализатор алгоритма