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

Материал из Викиконспекты
Перейти к: навигация, поиск
(Корректность)
(оформление англоязычных терминов, псевдокода и источников информации)
Строка 1: Строка 1:
'''Обход в ширину''' ('''Поиск в ширину''', '''BFS''', '''Breadth-first search''') — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.
+
'''Обход в ширину''' (Поиск в ширину, англ. ''BFS'', ''Breadth-first search'') — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.
  
 
== Алгоритм ==
 
== Алгоритм ==
Строка 43: Строка 43:
 
=== Реализация ===
 
=== Реализация ===
  
В приведенном ниже псевдокоде <tex> G = (V, E) </tex> - входной граф, <tex> s </tex> - выделенная вершина, Q - очередь. Множество <tex> X </tex> не хранится, вместо него используются расстояния в дереве обхода в ширину; расстояние от <tex>s</tex> до вершины <tex>u</tex>, вычисляемое алгоритмом, хранится в поле <tex>d[u]</tex>.
+
Предложенная ниже функция возвращает расстояние между вершинами <tex> source </tex> и <tex> target </target>. <tex>E</tex> - список ребер, Q - очередь. Множество <tex> X </tex> не хранится, вместо него используются расстояния в дереве обхода в ширину. ЗаметимЮ что расстояние от вершины <tex>source</tex> до вершины <tex>u</tex>, хранится в поле <tex>d[u]</tex>.
  
  '''BFS'''(<tex>G</tex>, <tex>s</tex>)
+
  '''int''' '''BFS'''(E: '''list'''<'''int''', '''int'''>, source: '''int''', destination: '''int''')
1    d[s] <tex> \leftarrow </tex> 0
+
    d.fill(<tex> \infty </tex>)
2    Q <tex> \leftarrow \emptyset </tex>
+
    d[source] = 0
3    Q.push(s)
+
    Q = <tex> \varnothing </tex>
4    '''while''' Q <tex> \ne \emptyset </tex>  
+
    Q.push(source)
5      '''do''' u <tex> \leftarrow </tex> Q.pop
+
    '''while''' Q <tex> \ne \varnothing </tex>  
6        '''for''' v: uv <tex> \in </tex> E
+
        q = Q.pop()
7          '''do''' '''if''' d[v] = <tex> \infty </tex>
+
        '''for''' <q, v> '''in''' E
8            '''then''' d[v] <tex> \leftarrow </tex> d[u] + 1
+
            '''if''' v == <tex> \infty </tex>
9                Q.push(v)
+
                d[v] = d[q] + 1
 +
                Q.push(v)
 +
    '''return''' d[destination]
  
== Ссылки ==
+
== Источники информации ==
  
 +
* Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 459. — ISBN 5-8489-0857-4
 
* [http://e-maxx.ru/algo/bfs Поиск в ширину на e-maxx.ru]
 
* [http://e-maxx.ru/algo/bfs Поиск в ширину на e-maxx.ru]
* [http://ru.wikipedia.org/wiki/Поиск_в_ширину Поиск в ширину в Википедии]
+
* [[wikipedia:en:Breadth-first_search| Wikipedia {{---}} Breadth-first search]]
 +
* [[wikipedia:ru:Поиск_в_ширину| Wikipedia {{---}} Поиск в ширину]]
 
* [http://rain.ifmo.ru/cat/view.php/vis/graph-general/bfs-2002 Визуализатор алгоритма]
 
* [http://rain.ifmo.ru/cat/view.php/vis/graph-general/bfs-2002 Визуализатор алгоритма]
 
== Литература ==
 
 
*Томас Х. Кормен и др. Алгоритмы: построение и анализ = INTRODUCTION TO ALGORITHMS. — 2-е изд. — М.: «Вильямс», 2006. — С. 1296. — ISBN 0-07-013151-1
 
  
  
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Кратчайшие пути в графах]]
 
[[Категория: Кратчайшие пути в графах]]

Версия 20:17, 2 декабря 2014

Обход в ширину (Поиск в ширину, англ. 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\limits_{v \in V} deg\ v = 2|E| [/math], то время, используемое на работу с ребрами, составляет [math] O(|E|) [/math]. Поэтому общее время работы алгоритма поиска в ширину — [math] O(|V| + |E|) [/math].

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

Утверждение:
В очереди поиска в ширину расстояние вершин до [math]s[/math] монотонно неубывает.
[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] u [/math] — вершина [math] w [/math].

Так как [math] w [/math] — предок [math] u [/math] в кратчайшем пути, то [math] \rho(s, u) = \rho(s, w) + 1 \gt \rho(s, w) [/math], и расстояние до w найдено верно, [math] \rho(s, w) = d[w] [/math]. Значит, [math] \rho(s, u) = d[w] + 1 [/math].

Так как [math] v [/math] — предок [math] u [/math] в дереве обхода в ширину, то [math] d[u] = d[v] + 1 [/math].

Расстояние до [math] u [/math] найдено некорректно, поэтому [math] \rho(s, u) \lt d[u] [/math]. Подставляя сюда два последних равенства, получаем [math] d[w] + 1 \lt d[v] + 1 [/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] source [/math] и [math] target \lt /target\gt . \lt tex\gt E[/math] - список ребер, Q - очередь. Множество [math] X [/math] не хранится, вместо него используются расстояния в дереве обхода в ширину. ЗаметимЮ что расстояние от вершины [math]source[/math] до вершины [math]u[/math], хранится в поле [math]d[u][/math].

int BFS(E: list<int, int>, source: int, destination: int)
    d.fill([math] \infty [/math])
    d[source] = 0
    Q = [math] \varnothing [/math]
    Q.push(source)
    while Q [math] \ne \varnothing [/math] 
        q = Q.pop()
        for <q, v> in E
            if v == [math] \infty [/math]
                d[v] = d[q] + 1
                Q.push(v)
    return d[destination]

Источники информации