Обход в ширину — различия между версиями
AKhimulya (обсуждение | вклад) м (→Источники информации) |
AKhimulya (обсуждение | вклад) (fixes) |
||
Строка 3: | Строка 3: | ||
== Описание алгоритма == | == Описание алгоритма == | ||
− | Пусть задан невзвешенный граф <tex> G = (V, E) </tex>, в котором выделена исходная вершина <tex>s</tex>. | + | Пусть задан невзвешенный ориентированный граф <tex> G = (V, E) </tex>, в котором выделена исходная вершина <tex>s</tex>. Требуется найти длину кратчайшего пути (если таковой имеется) от одной заданной вершины до другой. Частным случаем указанного графа является невзвешенный неориентированный граф, т.е. граф, в котором для каждого ребра найдется обратное, соединяющее те же вершины в другом направлении. |
− | Поиск в ширину также может построить дерево поиска в ширину. Изначально оно состоит из одного корня <tex> s </tex>. Когда мы добавляем непосещенную вершину в очередь, то добавляем ее и ребро, по которому мы до нее дошли, в дерево. Поскольку каждая вершина может быть посещена не более одного раза, она имеет не более одного родителя. После окончания работы алгоритма для каждой достижимой из <tex> s </tex> вершины <tex> t </tex> путь в дереве поиска в ширину соответствует кратчайшему пути от <tex> s </tex> до <tex> t </tex> в <tex> G </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> 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> 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> является избыточной, и его можно не хранить. | ||
Строка 11: | Строка 13: | ||
== Анализ времени работы == | == Анализ времени работы == | ||
− | Оценим время работы для входного графа <tex>G = (V, E)</tex>. В очередь добавляются только непосещенные вершины, поэтому каждая вершина посещается не более одного раза. Операции внесения в очередь и удаления из нее требуют <tex> O(1) </tex> времени, так что общее время работы с очередью составляет <tex> O(|V|) </tex> операций. Для каждой вершины <tex> v </tex> рассматривается не более <tex> deg | + | Оценим время работы для входного графа <tex>G = (V, E)</tex>, где <tex> E </tex> представлены списком смежности. В очередь добавляются только непосещенные вершины, поэтому каждая вершина посещается не более одного раза. Операции внесения в очередь и удаления из нее требуют <tex> O(1) </tex> времени, так что общее время работы с очередью составляет <tex> O(|V|) </tex> операций. Для каждой вершины <tex> v </tex> рассматривается не более <tex> \mathrm{deg}(v) </tex> ребер, инцидентных ей. Так как <tex> \sum\limits_{v \in V} \mathrm{deg}(v) = 2|E| </tex>, то время, используемое на работу с ребрами, составляет <tex> O(|E|) </tex>. Поэтому общее время работы алгоритма поиска в ширину — <tex> O(|V| + |E|) </tex>. |
== Корректность == | == Корректность == | ||
Строка 21: | Строка 23: | ||
Докажем это утверждение индукцией по числу выполненных алгоритмом шагов. | Докажем это утверждение индукцией по числу выполненных алгоритмом шагов. | ||
− | База: изначально очередь содержит только одну вершину <tex> s </tex> с расстоянием 0, утверждение верно. | + | '''База''': изначально очередь содержит только одну вершину <tex> s </tex> с расстоянием <tex> 0 </tex>, утверждение верно. |
− | Переход: пусть после <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, что соответствует нашему инварианту. | + | '''Переход''': пусть после <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> (возможно, <tex> 0 </tex>) вершин с расстоянием <tex> k </tex> и <tex> q + r </tex> вершин с расстоянием <tex> k + 1 </tex>, что соответствует нашему инварианту. |
}} | }} | ||
Строка 41: | Строка 43: | ||
== Реализация == | == Реализация == | ||
− | Предложенная ниже функция возвращает расстояние между вершинами source и | + | Предложенная ниже функция возвращает кратчайшее расстояние между двумя вершинами. |
+ | <ul> | ||
+ | <li><tex> \mathtt{source} </tex> — исходная вершина</li> | ||
+ | <li><tex> \mathtt{destination} </tex> — конечная вершина</li> | ||
+ | <li><tex> \mathtt{G} </tex> — граф, состоящий из списка вершин <tex> \mathtt{V} </tex> и списка смежности <tex> \mathtt{E} </tex>. Вершины нумеруются целыми числами.</li> | ||
+ | <li><tex> \mathtt{Q} </tex> — очередь.</li> | ||
+ | <li>В поле <tex> \mathtt{d[u]} </tex> хранится расстояние от <tex> \mathtt{source} </tex> до <tex> \mathtt{u} </tex>.</li> | ||
+ | </ul> | ||
− | '''int''' '''BFS'''(E: | + | '''int''' '''BFS'''(E: <V, E>, source: '''int''', destination: '''int''') |
+ | d = int[|V|] | ||
d.fill(<tex> \infty </tex>) | d.fill(<tex> \infty </tex>) | ||
d[source] = 0 | d[source] = 0 | ||
Строка 49: | Строка 59: | ||
Q.push(source) | Q.push(source) | ||
'''while''' Q <tex> \ne \varnothing </tex> | '''while''' Q <tex> \ne \varnothing </tex> | ||
− | + | u = Q.pop() | |
− | '''for''' | + | '''for''' vu '''in''' E |
'''if''' d[v] == <tex> \infty </tex> | '''if''' d[v] == <tex> \infty </tex> | ||
d[v] = d[q] + 1 | d[v] = d[q] + 1 | ||
Строка 56: | Строка 66: | ||
'''return''' d[destination] | '''return''' d[destination] | ||
− | == | + | Если требуется найти расстояние лишь между двумя вершинами, из функции можно выйти, как только будет установлено значение <tex> \mathtt{d[destination]} </tex>. |
+ | |||
+ | == Вариации алгоритма == | ||
=== 0-1 BFS === | === 0-1 BFS === | ||
− | Пусть в графе разрешены ребра веса 0 и 1, необходимо найти кратчайший путь между двумя вершинами. Для решения данной задачи модифицируем приведенный выше алгоритм следующим образом: вместо очереди будем использовать [[Персистентный_дек| | + | Пусть в графе разрешены ребра веса <tex> 0 </tex> и <tex> 1 </tex>, необходимо найти кратчайший путь между двумя вершинами. Для решения данной задачи модифицируем приведенный выше алгоритм следующим образом: вместо очереди будем использовать [[Персистентный_дек|deque]] (steque), а вместо добавления вершины в конец будем добавлять вершину в начало, если рассматриваемое ее ребро имеет вес <tex> 0 </tex>, а иначе в конец. Соответственно релаксируем расстояние до вершины. Таким образом, в начале дека всегда будет вершина, расстояние до которой меньше либо равно расстоянию до остальных вершин дека, и инвариант [[#Корректность | расположения элементов в деке в порядке неубывания]] сохраняется. Значит, алгоритм корректен на том же основании, что и обычный BFS. Очевидно, что каждая вершина войдет в дек не более двух раз, значит, асимптотика у данного алгоритма та же, что и у обычного BFS. |
=== 1-k BFS === | === 1-k BFS === | ||
− | Пусть в графе разрешены ребра веса <tex>1..k</tex>, необходимо найти кратчайший путь между двумя вершинами. Представим ребро <tex>uv</tex> веса <tex>m</tex> как последовательность ребер <tex>uu_1u_2..u_{m - 1}v</tex> (где <tex>u_1..u_{m - 1}</tex> - новые вершины). Применим данную операцию ко всем ребрам графа <tex>G(V, E)</tex>. Получим граф, состоящий (в худшем случае) из <tex>k|E|</tex> ребер и <tex>|V| + (k - 1)|E|</tex> вершин. Для нахождения кратчайшего пути следует запустить BFS на новом графе. Асимптотикой данного алгоритма является <tex> O(|V| + | + | Пусть в графе разрешены ребра целочисленного веса из отрезка <tex>1..k</tex>, необходимо найти кратчайший путь между двумя вершинами. Представим ребро <tex>uv</tex> веса <tex>m</tex> как последовательность ребер <tex>uu_1u_2..u_{m - 1}v</tex> (где <tex>u_1..u_{m - 1}</tex> - новые вершины). Применим данную операцию ко всем ребрам графа <tex>G(V, E)</tex>. Получим граф, состоящий (в худшем случае) из <tex>k|E|</tex> ребер и <tex>|V| + (k - 1)|E|</tex> вершин. Для нахождения кратчайшего пути следует запустить BFS на новом графе. Асимптотикой данного алгоритма является <tex> O(|V| + k|E|) </tex>. |
== Источники информации == | == Источники информации == |
Версия 18:56, 3 декабря 2014
Обход в ширину (Поиск в ширину, англ. BFS, Breadth-first search) — один из простейших алгоритмов обхода графа, являющийся основой для многих важных алгоритмов для работы с графами.
Содержание
Описание алгоритма
Пусть задан невзвешенный ориентированный граф
, в котором выделена исходная вершина . Требуется найти длину кратчайшего пути (если таковой имеется) от одной заданной вершины до другой. Частным случаем указанного графа является невзвешенный неориентированный граф, т.е. граф, в котором для каждого ребра найдется обратное, соединяющее те же вершины в другом направлении.Для алгоритма нам потребуются очередь, которая сначала содержит только , и множество посещенных вершин , которое изначально тоже содержит только . На каждом шаге алгоритм вынимает из начала очереди вершину, рассматривает все исходящие из нее ребра и добавляет все связанные с ней непосещенные вершины в и в конец очереди. Если очередь пуста, то алгоритм завершает работу.
Поиск в ширину также может построить дерево поиска в ширину. Изначально оно состоит из одного корня . Когда мы добавляем непосещенную вершину в очередь, то добавляем ее и ребро, по которому мы до нее дошли, в дерево. Поскольку каждая вершина может быть посещена не более одного раза, она имеет не более одного родителя. После окончания работы алгоритма для каждой достижимой из вершины путь в дереве поиска в ширину соответствует кратчайшему пути от до в .
Также можно для каждой вершины
считать длину этого пути, равную . Можно считать, что для непосещенных вершин эта длина бесконечно велика. Тогда на каждом шаге длина пути до равна , если посещена и в противном случае. Отсюда следует, что если на каждом шаге обновлять длины путей, то информация о множестве является избыточной, и его можно не хранить.Анализ времени работы
Оценим время работы для входного графа
, где представлены списком смежности. В очередь добавляются только непосещенные вершины, поэтому каждая вершина посещается не более одного раза. Операции внесения в очередь и удаления из нее требуют времени, так что общее время работы с очередью составляет операций. Для каждой вершины рассматривается не более ребер, инцидентных ей. Так как , то время, используемое на работу с ребрами, составляет . Поэтому общее время работы алгоритма поиска в ширину — .Корректность
Утверждение: |
В очереди поиска в ширину расстояние вершин до монотонно неубывает. |
Докажем это утверждение индукцией по числу выполненных алгоритмом шагов. База: изначально очередь содержит только одну вершину Переход: пусть после с расстоянием , утверждение верно. -ого шага алгоритма очередь содержит вершин с расстоянием и вершин с расстоянием . Тогда на -ом шаге мы извлечем из очереди одну вершину и добавим в нее все непосещенные( вершин), связанные с ней; расстояние до них, очевидно, будет равно . У нас останется (возможно, ) вершин с расстоянием и вершин с расстоянием , что соответствует нашему инварианту. |
Теорема: |
Алгоритм поиска в ширину в невзвешенном графе находит длины кратчайших путей до всех достижимых вершин. |
Доказательство: |
Допустим, что это не так. Выберем из вершин, для которых кратчайшие пути от найдены некорректно, ту, настоящее расстояние до которой минимально. Пусть это вершина , и она имеет своим предком в дереве обхода в ширину , а предок в кратчайшем пути до — вершина .Так как — предок в кратчайшем пути, то , и расстояние до w найдено верно, . Значит, .Так как Расстояние до — предок в дереве обхода в ширину, то . найдено некорректно, поэтому . Подставляя сюда два последних равенства, получаем , то есть, . Из ранее доказанной леммы следует, что в этом случае вершина попала в очередь и была обработана раньше, чем . Но она соединена с , значит, не может быть предком в дереве обхода в ширину, мы пришли к противоречию, следовательно, найденные расстояния до всех вершин являются кратчайшими. |
Реализация
Предложенная ниже функция возвращает кратчайшее расстояние между двумя вершинами.
- — исходная вершина
- — конечная вершина
- — граф, состоящий из списка вершин и списка смежности . Вершины нумеруются целыми числами.
- — очередь.
- В поле хранится расстояние от до .
int BFS(E: <V, E>, source: int, destination: int) d = int[|V|] d.fill() d[source] = 0 Q = Q.push(source) while Q u = Q.pop() for vu in E if d[v] == d[v] = d[q] + 1 Q.push(v) return d[destination]
Если требуется найти расстояние лишь между двумя вершинами, из функции можно выйти, как только будет установлено значение
.Вариации алгоритма
0-1 BFS
Пусть в графе разрешены ребра веса deque (steque), а вместо добавления вершины в конец будем добавлять вершину в начало, если рассматриваемое ее ребро имеет вес , а иначе в конец. Соответственно релаксируем расстояние до вершины. Таким образом, в начале дека всегда будет вершина, расстояние до которой меньше либо равно расстоянию до остальных вершин дека, и инвариант расположения элементов в деке в порядке неубывания сохраняется. Значит, алгоритм корректен на том же основании, что и обычный BFS. Очевидно, что каждая вершина войдет в дек не более двух раз, значит, асимптотика у данного алгоритма та же, что и у обычного BFS.
и , необходимо найти кратчайший путь между двумя вершинами. Для решения данной задачи модифицируем приведенный выше алгоритм следующим образом: вместо очереди будем использовать1-k BFS
Пусть в графе разрешены ребра целочисленного веса из отрезка
, необходимо найти кратчайший путь между двумя вершинами. Представим ребро веса как последовательность ребер (где - новые вершины). Применим данную операцию ко всем ребрам графа . Получим граф, состоящий (в худшем случае) из ребер и вершин. Для нахождения кратчайшего пути следует запустить BFS на новом графе. Асимптотикой данного алгоритма является .Источники информации
- Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 459. — ISBN 5-8489-0857-4
- MAXimal :: algo :: Поиск в ширину
- Wikipedia — Breadth-first search
- Wikipedia — Поиск в ширину
- Визуализатор алгоритма