Построение компонент вершинной двусвязности — различия между версиями
Novik (обсуждение | вклад) м (→Псевдокод второго прохода) |
Novik (обсуждение | вклад) (→Однопроходный алгоритм) |
||
Строка 47: | Строка 47: | ||
Значит все дуги <tex> V' </tex> будут будут добавлены в стек после дуги ведущей из точки сочленения в блок. В стеке в момент обнаружения точки сочленения будут находиться только дуги блока, связанного с ней, т.к. блоки найденные до него (если таковые имеются) будет уже извлечены из стека и покрашены в свой цвет.<br> | Значит все дуги <tex> V' </tex> будут будут добавлены в стек после дуги ведущей из точки сочленения в блок. В стеке в момент обнаружения точки сочленения будут находиться только дуги блока, связанного с ней, т.к. блоки найденные до него (если таковые имеются) будет уже извлечены из стека и покрашены в свой цвет.<br> | ||
=== Псевдокод === | === Псевдокод === | ||
− | + | '''function''' <tex>dfs</tex>(<tex>v</tex>, parent): | |
− | + | tin[<tex>v</tex>] = up[<tex>v</tex>] = time++ | |
− | + | '''for''' <tex> u : (v, u) \in E</tex>: | |
− | + | '''if''' <tex>u</tex> == parent | |
− | + | '''continue''' | |
− | + | '''if''' '''not''' visited[<tex>u</tex>] | |
− | + | stack.push(<tex>vu</tex>) | |
− | + | dfs(<tex>u, v</tex>) | |
− | + | '''if''' up[<tex>u</tex>] <tex>\geqslant</tex> tin[<tex>v</tex>] | |
− | + | color = maxColor++ | |
− | + | '''while''' stack.top() != (<tex>vu</tex>) | |
− | + | colors[stack.top()] = color | |
− | + | stack.pop() | |
− | + | colors[<tex>vu</tex>] = color | |
− | + | stack.pop() | |
− | + | '''if''' up[<tex>u</tex>] < up[<tex>v</tex>] | |
− | + | up[<tex>v</tex>] = up[<tex>u</tex>] | |
− | + | '''else''' '''if''' tin[<tex>u</tex>] < tin[<tex>v</tex>] | |
− | + | stack.push(<tex>vu</tex>) | |
− | + | '''else''' '''if''' up[<tex>v</tex>] > tin[<tex>u</tex>] | |
− | + | up[<tex>v</tex>] = up[<tex>u</tex>] | |
− | + | ||
− | + | '''for''' <tex> v \in V</tex>: | |
− | + | '''if''' '''not''' visited[<tex>v</tex>] | |
− | + | time = 0 | |
− | + | dfs(<tex>v</tex>, -1) | |
− | |||
<br> | <br> | ||
− | Во время алгоритма совершается один проход <tex>dfs</tex>, который работает за <tex>O(V + E)</tex>. Внутри него совершается еще цикл, который суммарно выполняет <tex>O(E)</tex> операций, т.к. каждое ребро может быть добавлено в стек только один раз. Следовательно, общее время работы алгоритма <tex>O(V + E) + O(E) = O(V + E)</tex> | + | Во время алгоритма совершается один проход <tex>dfs</tex>, который работает за <tex>O(|V| + |E|)</tex>. Внутри него совершается еще цикл, который суммарно выполняет <tex>O(|E|)</tex> операций, т.к. каждое ребро может быть добавлено в стек только один раз. Следовательно, общее время работы алгоритма <tex>O(|V| + |E|) + O(|E|) = O(|V| + |E|)</tex> |
== Источники информации == | == Источники информации == |
Версия 22:31, 10 ноября 2015
Содержание
Двупроходный алгоритм
Найти компоненты вершинной двусвязности неориентированного графа можно с помощью обхода в глубину.
Первый проход:
Используем первый проход, чтобы найти точки сочленения.
Второй проход:
Точка сочленения принадлежит как минимум двум компонентам вершинной двусвязности.
Вершина является точкой сочленения, если у нее есть сын .
Это также значит, что ребро содержится в другой компоненте вершинной двусвязности, нежели ребро по которому мы пришли в вершину , используя поиск в глубину. Получается, что перейдя по этому ребру, мы окажемся в другой компоненте вершинной двусвязности.
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.
Псевдокод второго прохода
function( , color, parent): for : if == parent continue if not visited[ ] if up[ ] tin[ ] newColor = maxColor++ col[ ] = newColor dfs( , newColor, ) else col[ ] = color dfs( , color, ) else if up[ ] tin[ ] col[ ] = color for : if not visited[ ] dfs( , -1, -1) |
Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.
В алгоритме выполняется два прохода , каждый из которых работает . Значит время работы алгоритма .
Однопроходный алгоритм
Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека.
Таким образом, каждый раз находя компоненту вершинной двусвязности мы сможем покрасить все ребра, содержащиеся в ней, в новый цвет.
Доказательство корректности алгоритма
Предположим, что граф содержит точку сочленения
- Все вершины являются потомками в дереве обхода;
- Все вершины будут пройдены в течение периода серого состояния ;
- В
Значит все дуги
Псевдокод
function( , parent): tin[ ] = up[ ] = time++ for : if == parent continue if not visited[ ] stack.push( ) dfs( ) if up[ ] tin[ ] color = maxColor++ while stack.top() != ( ) colors[stack.top()] = color stack.pop() colors[ ] = color stack.pop() if up[ ] < up[ ] up[ ] = up[ ] else if tin[ ] < tin[ ] stack.push( ) else if up[ ] > tin[ ] up[ ] = up[ ]
for: if not visited[ ] time = 0 dfs( , -1)
Во время алгоритма совершается один проход , который работает за . Внутри него совершается еще цикл, который суммарно выполняет операций, т.к. каждое ребро может быть добавлено в стек только один раз. Следовательно, общее время работы алгоритма
Источники информации
- В.А.Кузнецов, А.М.Караваев. "Оптимизация на графах" - Петрозаводск, Издательство ПетрГУ 2007