Поиск в глубину начинает посещение вершин графа с произвольной вершины, помечая ее как посещенную. На каждой итерации алгоритм обрабатывает непо- сещенные вершины, смежные с текущей. Если таких вершин несколько, они обрабатываются в произвольном порядке. Этот процесс продолжается до достижения тупика — вершины, у которой нет непосещенных смежных вершин.
По достижении тупика алгоритм возвращается на одно ребро назад (в вершину, из которой он попал в тупик) и пытается продолжить посещения смежных непосещенных вершин из этого места. В конце концов алгоритм прекращает работу, когда возвращается в начальную вершину, которая к этому моменту становится тупиком.
В этот момент все вершины данного компонента связности графа оказываются посещенными. Если в графе после этого имеются непосещенные вершины, процесс должен повториться, при этом в качестве стартовой используется любая из непосещенных вершин.
Для выполнения поиска в глубину удобно использовать стек. Мы помещаем вершину в стек, когда впервые посещаем ее (т.е. когда начинается обход вершины), и убираем ее из стека, когда она становится тупиком (т.е. обход вершины завершается).
Алгоритм DFS (G)
// Входные данные: Граф G=(V,E)
// Выходные данные: Граф G, вершины которого пронумерованы
// последовательными числами в порядке их
// первого посещения в процессе поиска
Помечаем все вершины в V числом 0 (непосещенная вершина)
count←0
for всех вершин v∈V do
if вершина v помечена 0
quad quad "dfs1"(v)
Где dfs1(v)
// Рекурсивно посещает все непосещенные вершины, связанные
// с вершиной v, и присваивает им номера в порядке посещения
// при помощи глобальной переменной count
count larr count + 1
Помечаем вершину v числом count
for каждой вершины w из V, смежной с v do
quad if вершина w помечена 0
quad quad quad "dfs1"(w)
Нетрудно увидеть, что этот алгоритм достаточно эффективен, поскольку время его работы пропорционально размеру используемой для представления графа структуры данных. Так, для представления с использованием матрицы смежности временная эффективность обхода равна Theta(|V|^2), а для представления с использованием списков смежных вершин – Theta(|V| + |E|), где |V| и |E| — соответственно, количество вершин и ребер графа.
Важным применением поиска в глубину являются проверка связности графа и его ацикличности.
Поскольку поиск в глубину завершается после посещения всех вершин, соединенных некоторым путем со стартовой, проверку связности графа можно выполнить следующим образом. Начнем поиск в глубину с произвольной вершины и после завершения проверим, все ли вершины графа посещены. Если все, то граф связный, в противном случае — нет.
Для проверки наличия циклов в графе можно сначала в dfs1
присваивать вершине число -1, а
давать номер только тогда, когда вершина становится тупиковой (после цикла).
Тогда обнаружение смежной вершины w, помеченной -1, означает, что в графе есть цикл.
Если граф является ориентированным, то для него может поставлена задача топологической сортировки, которая состоит в следующем: указать такой линейный порядок на его вершинах, чтобы любая дуга вела от вершины с меньшим номером к вершине с большим номером. Очевидно, что если в графе есть циклы, то такого порядка не существует. Обращение порядка номеров в DFS для поиска циклов дает нам решение задачи топологической сортировки.
Поиск в ширину работает "кругами", сперва посещая все вершины, смежные со стартовой, затем — вершины, которые лежат на расстоянии двух ребер от стартовой, и т.д., пока не будут посещены все вершины связного компонента, которому принадлежит стартовая точка.
Для выполнения поиска в ширину удобно использовать очередь. Очередь инициализируется стартовой вершиной поиска, помеченной как посещенная. На каждой итерации алгоритм находит все непосещенные вершины, смежные с вершиной в начале очереди, помечает их как посещенные и вносит в очередь; после этого вершина в начале очереди изымается из нее.
Алгоритм BFS (G)
// Входные данные: Граф G=(V,E)
// Выходные данные: Граф G, вершины которого пронумерованы
// последовательными числами в порядке их
// первого посещения в процессе поиска
Помечаем все вершины в V числом 0 (непосещенная вершина)
count larr 0
for всех вершин v in V do
quad if вершина v помечена 0
quad quad "bfs1"(v)
Где bfs1(v)
// Обход всех вершин, связанных с v и назначение им номеров
// в порядке посещения при помощи глобальной переменной count
"mark"(v)
while очередь не пуста do
quad Извлекаем из очереди первый элемент и помещаем его в v
quad for каждой вершины w из V, смежной с v do
quad quad if вершина w помечена 0
quad quad quad "mark"(w)
Где mark(v)
count larr count + 1
Помечаем вершину v числом count
Добавляем v в очередь
Поиск в ширину имеет ту же эффективность, что и поиск в глубину.
Поиск в ширину может использоваться для проверки связности графа и при поиске пути между двумя вершинами, состоящего из минимального количества ребер.