2. Связность
Известны два классических алгоритма установления связности обыкновенный графов.
Алгоритм Крускала (Kruskal)
Пусть имеем граф g=(V,X). Пусть Т – подмножество ребер графа g, которое вначале пустое. Добавим к Т первое попавшееся ребро, если оно не образует цикл с уже имеющимися в Т ребрами. Продолжим процесс до исчерпания подходящих ребер. Если после завершения процесса число ребер в Т равно n-1 , тогда граф g связный.
Алгоритм Прима (Prim)
Пусть имеем граф g=(V,X). Пусть Т – подмножество его ребер вместе с конечными вершинами, которое вначале пустое. Добавим к Т первое попавшееся ребро. Далее, будем добавлять к Т ребро, одна вершина котрого находится в Т, а другая за пределами Т. Если по завершении процесса число ребер в Т равно n-1 , граф связный.
Определение связности на основе волнового процесса
Процедуру определения связности графа можно построить на основе первого волнового процесса. Выберем произвольную вершину a и будем считать, что V0={a}. Выполним разметку (индексирование) вершин на основе волнового процесса 1. Если размеченными оказались все вершины, то граф является связным.
Пусть <V0,V1, ... , Vs> - разбиение множества вершин графа, получемое в результате волнового процесса и пусть vi – вершина номер i графа g, а vik – вершина, имеющая номер k в пределах подмножества Vi . Тогда анализ процедуры волнового процесса дает следующее соотношение для времени его выполнения:
,
где n – число вершин, r – число ребер, p(vik), p(vi) – степень вершины, s – количество подмножеств, порождаемых волновым процессом. Отсюда для нашего алгоритма получаем:
t = F(n) = O(r).
Таким образом, по времени выполнения, последний алгоритм имеет такую же эффективность, как и алгоритмы Крускала и Прима.
Определение компонент связности
Следующий алгоритм позволяет определить все компоненты связности графа.
1. Выбрать произвольно вершину a заданного графа g = (V,X). Полагая V0={a}выполнить волновой процесс с индексированием вершин и пусть V0,V1, ... , Vs - полученная при этом последовательность подмножеств. Разделим множество вершин V на два подмножества: U1 = V0V, ... , Vs и U2 = V - U1 .
2. Выделить подграф g1 на подмножестве вершин U1 и подграф g2 на подмножестве вершин U2 . Подграф g1 – полученный компонент связности графа g.
3. Если U2 выполнить п.1 для подграфа g2, иначе – алгоритм завершить.
3. Задачи о кратчайших маршрутах
Пусть дан граф g = (V,X) и две его вершины a, bV. Имеется 4 вида задач о кратчайших маршрутах.
1 Найти длину кратчайшего маршрута из вершины a в вершину b.
2 Найти маршрут минимальной длины из a в b.
3 Найти количество кратчайших маршрутов из a в b.
4 Найти множество всех кратчайших маршрутов из a в b.
Под длиной маршрута в графе понимают количество ребер, его составляющих.
Алгоритмы решения указанных задач могут быть построены на основе волновых процессов.
Задача о кратчайших маршрутах относятся к классу P.
Задача 1
Будем выполнять волновой процесс 1 начиная с V0 = {a}. Завершим его в том случае, если очередное подмножество Vk будет включать вершину b. Индекс k этого подмножества и будет искомой длиной кратчайшего пути.
Ниже, в качестве примера программной реализации такого алгоритма, приведена функция dist(g,i,m) из библиотеки ALGRAPH/C++ (в несколько упрощенном варианте), которая находит длину кратчайшего маршрута из вершины i в вершину m. Если маршрут для указанных вершин отсутствует, функция возвращает значение -1.
int dist(graf g, int i, int m)
{ int a,b,j,k,Nvv,Nw, L=0, n=g.nv;
if (i==m) return 0;
if (g.p[i]==0) return -1;
byte* Vs= new byte[n];
int *vv= new int[n],
*w = new int[n];
for (k=0;k<n;k++) Vs[k]=0;
Vs[i]=1; vv[0]=i; Nvv=1;
while (Nvv)
{ Nw=0; L++;
for (j=0;j<Nvv;j++)
{ a=vv[j];
for (k=0;k<g.p[a];k++)
{ b=g.r[a][k]; if (b==m) goto mm;
if (Vs[b]==0) { Vs[b]=1; w[Nw++]=b; }
}
}
for (j=0;j<Nw;j++) vv[j]=w[j];
Nvv=Nw;
}
L=-1;
mm:
delete[] Vs; delete[] vv; delete[] w;
return L;
}
Задача 2
Алгоритм поиска кратчайшего пути следует из свойств 2,3 волнового процесса 2.
Задача 3
Выполним волновой процесс 2. В результате получим последовательность подмножеств вершин V'0, V'1, ... , V'k, при этом V'0 = {a}, V'k = {b}. Пронумеруем вершины в каждом из подмножеств независимо. Каждой вершине из указанных подмножеств припишем два индекса так, чтобы запись vij обозначала вершину номер j из подмножества V'i. Очевидно, что v0,1 = a, vk,1 = b. Обозначим через (vij) вес вершины vij. Каждой вершине из подмножеств V'0, V'1, ... , V'k присвоим вес в соответствии со следующим правилом.
1. Положим (v0,1) = 1 .
2. , i = 0, 1, ... , k-1 .
Суммирование необходимо выполнять по всем значениям m, удовлетворяющих условию:
<vi,m , vi+1 , j> X .
Пункт 2 означает, что вес вершины vi+1 , j определяется как сумма индексов всех тех вершин из множества V'i , которые являются начальными по отношению к вершине vi+1 , j.
При выполнении п.2 следует соблюдать очередность: вначале определяются веса всех вершин из множества V'i, затем веса всех вершин из следующего за ним множества V'i+1 .
После завершения процедуры вес конечной вершины (vk,1) = (b) будет равен искомому количеству кратчайших путей.
Задача 4
Алгоритм решения этой задачи вытекает из свойства 3 волнового процесса 2.