Поиск в глубину
Поиск в глубину является обобщением метода обхода дерева в прямом порядке Предположим, что есть ориентированный граф G, в котором первоначально все вершины помечены как непосещенные. Поиск в глубину начинается с выбора начальной вершины v графа G, и эта вершина помечается как посещенная. Затем для каждой вершины, смежной с вершиной v и которая не посещалась ранее, рекурсивно применяется поиск в глубину. Когда все вершины, которые можно достичь из вершины v, будут «удостоены» посещения, поиск заканчивается. Если некоторые вершины остались не посещенными, то выбирается одна из них и поиск повторяется. Этот процесс продолжается до тех пор, пока обходом не будут охвачены все вершины орграфа G.
Этот метод обхода вершин орграфа называется поиском в глубину, поскольку поиск непосещенных вершин идет в направлении вперед (вглубь) до тех пор, пока это возможно. Например, пусть x – последняя посещенная вершина. Для продолжения процесса выбирается какая-либо нерассмотренная дуга x → y, выходящая из вершины x. Если вершина y уже посещалась, то ищется другая вершина, смежная с вершиной x.
Если вершина y ранее не посещалась, то она помечается как посещенная и поиск начинается заново от вершины y. Пройдя все пути, которые начинаются в вершине y, возвращаемся в вершину x, т. е. в ту вершину, из которой впервые была достигнута вершина y. Затем продолжается выбор нерассмотренных дуг, исходящих из вершины x, и так до тех пор, пока не будут исчерпаны все эти дуги (рис. 2).
Для представления вершин, смежных с вершиной v, можно использовать список смежных, а для определения вершин, которые ранее посещались, – массив Visited:
Graph: TAdjacencyList;
Visited: array[1..n] of boolean;
Чтобы применить эту процедуру к графу, состоящему из n вершин,
надо сначала присвоить всем элементам массива Visited значение false, затем начать поиск в глубину для каждой вершины, помеченной как false.
Procedure DepthSearch(v: integer);
begin
Visited[v] := true;
for каждой вершины y, смежной с v do
if not Visited[y] then
DepthSearch(y);
end;
begin
while есть непомеченные вершины do begin
v := любая непомеченная вершина;
DepthSearch(v);
end;
end.
Рисунок 2 – Поиск в глубину
Поиск в глубину для полного обхода графа с n вершинами и m дугами требует общего времени порядка O(max(n, m)). Поскольку обычно m ≥ n, то получается O(m).
Поиск в ширину (волновой алгоритм)
Этот алгоритм поиска в графе также называют волновым алгоритмом из-за того, что обход графа идет по принципу распространения волны. Волна растекается равномерно во все стороны с одинаковой скоростью. На i-м шаге будут помечены все вершины, достижимые за i ходов, если ходом считать переход из одной вершины в другую.
Метод поиска в ширину получается из программы поиска в глубину, если заменить стек возврата на очередь. Эта простая замена модифицирует порядок обхода вершин так, что обход идет равномерно во все стороны, а не вглубь как при поиске в глубину (рис. 3).
Здесь используются те же структуры Graph и Visited, что были
описаны в алгоритме поиска в глубину.
Procedure WidthSearch(v: integer);
var
Delayed: array[1..n] of integer; {Очередь}
Count, {Хвост очереди}
Head: integer; {Голова очереди}
Current, j: integer;
begin
Count := 1; Head := 0; Delayed[Count] := v;
Visited[v] := true;
repeat
Head := Head +1; Current := Delayed[Head];
for каждой вершины y, смежной с Current do
if not Visited[y] then begin
Count := Count + 1;
Delayed[Count] := Graph[y];
Visited[y] := true;
end;
until Count = Head;
end;
begin
while есть непомеченные вершины do begin
v := любая непомеченная вершина;
WidthSearch(v);
end;
end.
Рисунок 3 – Поиск в ширину
Поиск в ширину для полного обхода графа с n вершинами и m дугами требует столько же времени, как и поиск в глубину, т. е. времени порядка O(max(n, m)). Поскольку обычно m ≥ n, то получается O(m).