Объяснение алгоритма нахождения точек сочленения или срезанных вершин графа
Я искал сеть и не мог найти никакого объяснения алгоритма DFS для поиска всех вершин артикуляции графа. Нет даже вики-страницы.
из чтения вокруг, я должен знать основные факты отсюда. PDF
на каждом узле есть переменная, которая фактически смотрит на задние края и находит ближайший и самый верхний узел к корневому узлу. После обработки всех ребер он будет найден.
но я не поймите, как найти эту переменную down & up на каждом узле во время выполнения DFS. Что именно делает эта переменная?
пожалуйста, объясните алгоритм.
спасибо.
3 ответов
Поиск вершин артикуляции-это приложение DFS.
в двух словах
- применить DFS на графике. Получить дерево DFS.
- узел, который посещается ранее, является "родителем" тех узлов, которые достигаются им и посещаются позже.
- если у любого дочернего узла нет пути к любому из предков его родителя, это означает, что удаление этого узла сделает этот дочерний элемент непересекающимся из графика.
- есть исключение: корень дерева. Если у него больше одного ребенка, то это точка артикуляции, иначе нет.
пункт 3 по сути означает, что этот узел является точкой сочленения.
теперь для ребенка этот путь к предкам узла будет проходить через задний край от него или от любого из его потомков.
все это прекрасно объясняется в этом PDF.
я попытаюсь разработать интуитивное понимание того, как работает этот алгоритм, а также дать прокомментированный псевдокод, который выводит Бикомпоненты, а также мосты.
на самом деле легко разработать алгоритм грубой силы для точек сочленения. Просто выньте вершину и запустите BFS или DFS на графике. Если он остается подключенным, то вершина не является точкой сочленения, иначе это. Это будет работать в O(V(E+V)) = O(EV)
времени. Задача состоит в том, как это сделать в линейное время (т. е. O(E+V)
).
точки артикуляции соединяют два (или более) подграфа. Это означает, что нет ребер от одного подграфа к другому. Поэтому представьте, что вы находитесь в одном из этих подграфов и посещаете его узел. Когда вы посещаете узел, вы отмечаете его, а затем переходите к следующему неотмеченному узлу, используя некоторое доступное ребро. Пока вы это делаете, откуда вы знаете, что находитесь внутри все того же подграфа? Понимание здесь заключается в том, что если вы находитесь в том же подграфе, вы в конечном итоге увидите помеченный узел через ребро при посещении неповрежденного узла. Это называется задним краем и указывает, что у вас есть цикл. Как только вы найдете задний край, вы можете быть уверены, что все узлы через этот помеченный узел к тому, который вы посещаете прямо сейчас, являются частью одного и того же подграфа, и между ними нет точек сочленения. Если вы не видели задних кромок, то все узлы, которые вы посетили до сих пор, являются точками артикуляции.
нам нужен алгоритм, который посещает вершины и отмечает все точки между целью задних кромок как в настоящее время-быть посещаемым узлы, как в том же подграфе. Очевидно, что внутри подграфов могут быть подграфы, поэтому нам нужно выбрать самый большой подграф, который у нас есть. Эти подграфы называются BI-компоненты. Мы можем реализовать этот алгоритм, назначив каждому bi-компоненту идентификатор, который инициализируется как просто количество вершин, которые мы посетили до сих пор. Позже, когда мы найдем задние края, мы можем сбросить BI-compinent ID до самого низа мы пока не добрались.
мы, очевидно, нужно два прохода. В первом проходе мы хотим выяснить, какую вершину мы можем видеть из каждой вершины через задние ребра, если таковые имеются. Во втором проходе мы хотим посетить вершины в противоположном направлении и собрать минимальный BI-component ID (т. е. самый ранний предок, доступный от любых потомков). DFS, естественно, подходит здесь. В DFS мы сначала спускаемся, а затем возвращаемся, поэтому оба вышеуказанных прохода можно сделать в одном DFS поперечный.
Теперь без дальнейших церемоний, вот псевдокод:
time = 0
visited[i] = false for all i
GetArticulationPoints(u)
visited[u] = true
u.st = time++
u.low = v.st //keeps track of highest ancestor reachable from any descendants
dfsChild = 0 //needed because if no child then removing this node doesn't decompose graph
for each ni in adj[i]
if not visited[ni]
GetArticulationPoints(ni)
++dfsChild
parents[ni] = u
u.low = Min(u.low, ni.low) //while coming back up, get the lowest reachable ancestor from descendants
else if ni <> parent[u] //while going down, note down the back edges
u.low = Min(u.low, ni.st)
//For dfs root node, we can't mark it as articulation point because
//disconnecting it may not decompose graph. So we have extra check just for root node.
if (u.low = u.st and dfsChild > 0 and parent[u] != null) or (parent[u] = null and dfsChild > 1)
Output u as articulation point
Output edges of u with v.low >= u.low as bridges
output u.low as bicomponent ID
если low
потомка u
больше dfsnum
of u
, потом u
считается точкой артикуляции.
int adjMatrix[256][256];
int low[256], num=0, dfsnum[256];
void cutvertex(int u){
low[u]=dfsnum[u]=num++;
for (int v = 0; v < 256; ++v)
{
if(adjMatrix[u][v] && dfsnum[v]==-1)
{
cutvertex(v);
if(low[v]>dfsnum[u])
cout<<"Cut Vertex: "<<u<<"\n";
low[u]=min(low[u], low[v]);
}
else{
low[u]=min(low[u], dfsnum[v]);
}
}
}