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