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

Материал из Викиконспекты
Перейти к: навигация, поиск
(почти полностью переписал статью)
м
Строка 1: Строка 1:
 
{{В разработке}}
 
{{В разработке}}
'''Обход в ширину''' ('''Поиск в ширину, BFS''', Breadth-first search) — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.
+
'''Обход в ширину''' ('''Поиск в ширину, BFS''', '''Breadth-first search''') — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.
  
 
== Алгоритм ==
 
== Алгоритм ==
Строка 6: Строка 6:
 
=== Общая идея ===
 
=== Общая идея ===
  
Пусть задан невзвешенный граф <tex>G = (V, E)</tex>, в котором выделена исходная вершина <tex>s</tex>. Для алгоритма нам потребуются очередь, которая сначала содержит только <tex> s </tex>, и множество посещенных вершин <tex> X </tex>, которое изначально тоже содержит только <tex> s </tex>. На каждом шаге алгоритм вынимает из начала очереди вершину, рассматривает все исходящие из нее ребра и добавляет все связанные с ней непосещенные вершины в <tex> X </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> s </tex> вершины <tex> t </tex> путь в дереве поиска в ширину соответствует кратчайшему пути от <tex> s </tex> до <tex> t </tex> в <tex> G </tex>.
 
Поиск в ширину также может построить дерево поиска в ширину. Изначально оно состоит из одного корня <tex> s </tex>. Когда мы добавляем непосещенную вершину в очередь, то добавляем ее и ребро, по которому мы до нее дошли, в дерево. Поскольку каждая вершина может быть посещена не более одного раза, она имеет не более одного родителя. После окончания работы алгоритма для каждой достижимой из <tex> s </tex> вершины <tex> t </tex> путь в дереве поиска в ширину соответствует кратчайшему пути от <tex> s </tex> до <tex> t </tex> в <tex> G </tex>.
Строка 14: Строка 14:
 
== Анализ времени работы ==
 
== Анализ времени работы ==
  
Оценим время работы для входного графа <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>.
+
Оценим время работы для входного графа <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} deg\ v = 2|E| </tex>, то время, используемое на работу с ребрами, составляет <tex> O(|E|) </tex>. Поэтому общее время работы алгоритма поиска в ширину — <tex> O(|V| + |E|) </tex>.
  
 
=== Корректность ===
 
=== Корректность ===
Строка 20: Строка 20:
 
{{Утверждение
 
{{Утверждение
 
|statement=
 
|statement=
В алгоритме поиска в ширину очередь всегда содержит сначала вершины с расстоянием k, а потом, возможно, вершины с расстоянием k + 1.
+
В алгоритме поиска в ширину очередь всегда содержит сначала некоторое количество вершин с расстоянием k, а потом некоторое количество вершин с расстоянием k + 1(возможно, нулевое).
 
|proof=
 
|proof=
 
Докажем это утверждение индукцией по шагам алгоритма.
 
Докажем это утверждение индукцией по шагам алгоритма.
Строка 58: Строка 58:
  
 
[http://e-maxx.ru/algo/bfs Поиск в ширину на e-maxx.ru]
 
[http://e-maxx.ru/algo/bfs Поиск в ширину на e-maxx.ru]
 +
 
[http://ru.wikipedia.org/wiki/Поиск_в_ширину Поиск в ширину в Википедии]
 
[http://ru.wikipedia.org/wiki/Поиск_в_ширину Поиск в ширину в Википедии]
 +
 
[http://rain.ifmo.ru/cat/view.php/vis/graph-general/bfs-2002 Визуализатор алгоритма]
 
[http://rain.ifmo.ru/cat/view.php/vis/graph-general/bfs-2002 Визуализатор алгоритма]
  

Версия 04:44, 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} deg\ 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

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

Визуализатор алгоритма