Алгоритм для проверки, сильно ли связан направленный граф

Мне нужно проверить, является ли направленный граф сильно связный, или, другими словами, если все узлы могут быть достигнуты любым другим узлом (не обязательно через прямое ребро).

один из способов сделать это-запустить DFS и BFS на каждом узле и увидеть, что все остальные все еще достижимы.

есть ли лучший подход для этого?

8 ответов


Тарьяна это алгоритм сильно связанных компонентов (или Gabow это вариация), конечно, будет достаточно; если есть только один сильно связанный компонент, то график сильно связан.

оба линейном времени.

Как и при обычном первом поиске глубины, вы отслеживаете состояние каждого узла: новый, видимый, но все еще открытый (он находится в стеке вызовов), и видимый и законченный. Кроме того, вы сохраняете глубину при первом достижении узла и самая низкая такая глубина, которая достижима из узла (вы знаете это после завершения узла). Узел является корнем сильно связанного компонента, если наименьшая достижимая глубина равна его собственной глубине. Это работает, даже если глубина, на которую вы достигаете узла от корня, не является минимально возможной.

чтобы проверить, является ли весь граф одним SCC, инициируйте dfs с любого отдельного узла, и когда вы закончите, если наименьшая достижимая глубина равна 0, и каждый узел был посещен, тогда весь график сильно связан.


рассмотрим следующий алгоритм.


  1. начните со случайной вершины v графика G, и запустить DFS(G, v).

    • если DFS(G, v) не удается достичь каждой другой вершины в графе G, тогда есть некоторая вершина u, такой, что нет направленного пути от v до u, и таким образом G is не сильно связаны..

    • если он достигает каждая вершина, тогда есть направленный путь от v к каждой другой вершине в графе G.

  2. изменить направление всех ребер в ориентированном графе G.

  3. снова запустить DFS начиная с v.

    • если DFS не достигает каждой вершины, то есть некоторая вершина u, такой, что в исходном графике нет направленного путь от u to v.

    • С другой стороны, если он достигает каждой вершины, то в исходном графе существует направленный путь от каждой вершины u to v.

таким образом, если G "проходит" оба DFSs, он сильно связан. Кроме того, поскольку DFS работает в O(n + m) время, этот алгоритм работает в O(2(n + m)) = O(n + m) время, так как для этого требуется 2 обхода DFS.


вы можете посчитать Все Пары Кратчайший Путь и посмотреть, если любое бесконечно.


алгоритм Тарьяна уже упоминался. Но я обычно нахожу Kosaraju это легче следовать, даже если для этого требуется два обхода графика. IIRC, это также довольно хорошо объясняется в CLRS.


test-connected(G)
{
    choose a vertex x
    make a list L of vertices reachable from x,
    and another list K of vertices to be explored.
    initially, L = K = x.

    while K is nonempty
    find and remove some vertex y in K
    for each edge (y, z)
    if (z is not in L)
    add z to both L and K

    if L has fewer than n items
    return disconnected
    else return connected
}

один из способов сделать это - создать матрица Лапласа для графа, затем вычислить собственные значения, и, наконец, подсчитать количество нулей. Граф сильно связан, если существует только одно нулевое собственное значение.

Примечание: обратите внимание на несколько иной метод создания матрицы Лапласа для графа.


вы можете использовать простой алгоритм Kosaraju на основе DFS, который делает два обхода графа DFS:

идея в том, что если каждый узел может быть достигнут из вершины v, и каждый узел может достичь v, то граф сильно связан. На Шаге 2 алгоритма мы проверяем, достижимы ли все вершины из v. на шаге 4 мы проверяем, могут ли все вершины достигать v (в обратном графе, если все вершины достижимы из v, то все вершины могут достигать v в исходном диаграмма.)

: 1) инициализировать все вершины не побывала.

2) Выполните обход графа DFS, начиная с любой произвольной вершины v. Если обход DFS не посещает все вершины, то верните false.

3) отменить все дуги (или найти перенести или отменить график)

4) отметьте все вершины как не посещенные в обратном графе.

5) выполните обход DFS обратного графа, начиная с той же вершины v (то же, что и Шаг 2). Если DFS обход не посещает все вершины, а затем возвращать false. В противном случае возвращает true.

временная сложность: временная сложность вышеуказанной реализации такая же, как первый поиск глубины, который является O(V+E), если график представлен с использованием представления списка смежности.


чтобы проверить, имеет ли каждый узел оба пути к и от каждого другого узла в данном графике:

1. DFS / BFS со всех узлов:

Тарьяна это предполагает, что каждый узел имеет глубину d[i]. Изначально корень имеет наименьшую глубину. И мы делаем обновления DFS после заказа d[i] = min(d[j]) для любого соседа j of i. На самом деле BFS также отлично работает с правилом сокращения d[i] = min(d[j]) здесь.

function dfs(i)
    d[i] = i
    mark i as visited
    for each neighbor j of i: 
        if j is not visited then dfs(j)
        d[i] = min(d[i], d[j])

если есть путь пересылки от u to v, потом d[u] <= d[v]. В ГКК,d[v] <= d[u] <= d[v], таким образом, все узлы в SCC будут иметь одинаковую глубину. Чтобы сказать, является ли граф SCC, мы проверяем, имеют ли все узлы одинаковое d[i].

2. Два DFS / BFS из одного узла:

это упрощенная версия Kosaraju алгоритм. Начиная с корня, мы проверяем, может ли каждый узел быть достигнут DFS/BFS. Затем измените направление каждого края. Мы проверяем, может ли каждый узел быть снова из того же корня. См.код C++.