Построение компонент вершинной двусвязности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (Псевдокод второго прохода)
(Однопроходный алгоритм)
Строка 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>
'''Псевдокод:
+
=== Псевдокод ===
    dfs(<tex>v, parent</tex>)
+
    '''function''' <tex>dfs</tex>(<tex>v</tex>, parent):
         <tex>enter[v] \leftarrow return[v] \leftarrow time</tex>++
+
         enter[<tex>v</tex>] <tex>\leftarrow</tex> return[<tex>v</tex>] <tex>\leftarrow</tex> time++
         для всех  вершин <tex>u</tex> смежных <tex>v</tex>:
+
         '''for''' <tex> u \in V : (v, u) \in E</tex>:
             если (<tex>u</tex> родитель)
+
            '''if''' <tex>u</tex> == parent
                 переходим к следующей итерации
+
                continue
            если (<tex>u</tex> не посещена)
+
             '''if''' '''not''' visited[<tex>u</tex>]
                 добавить в стек ребро<tex>vu</tex>
+
                 stack.push(<tex>vu</tex>)
                dfs(<tex>u, v</tex>)
+
                 <tex>dfs</tex>(<tex>u, v</tex>)
                 если (<tex>return[u] >= enter[v]</tex>)
+
                 '''if''' return[<tex>u</tex>] <tex>\geqslant</tex> enter[<tex>v</tex>]
                     <tex>c \leftarrow </tex> новый цвет
+
                     color <tex>\leftarrow </tex> maxColor++
                     пока (ребро <tex>vu</tex> не равно вершине стека)
+
                     '''while''' stack.top() != <tex>vu</tex>
                         <tex>color</tex>[вершина стека] <tex> \leftarrow c</tex>
+
                         colors[stack.top()] <tex> \leftarrow </tex> color
                         извлечь вершину стека
+
                         stack.pop()
                     <tex>color[vu] \leftarrow c</tex>
+
                     colors[<tex>vu</tex>] <tex>\leftarrow </tex> color
                     извлечь вершину стека
+
                     stack.pop()
                 если (<tex>return[u] < return[v]</tex>)
+
                 '''if''' return[<tex>u</tex>] < return[<tex>v</tex>]
                     <tex>return[v] \leftarrow return[u]</tex>
+
                     return[<tex>v</tex>] <tex>\leftarrow</tex> return[<tex>u</tex>]
             иначе
+
             '''else'''
                 если (<tex>enter[u] < enter[v]</tex>)
+
                 '''if''' enter[<tex>u</tex>] < enter[<tex>v</tex>]
                    добавить в стек ребро <tex>vu</tex>
+
                    stack.push(<tex>vu</tex>)
                 если (<tex>return[v] > enter[u]</tex>)
+
                 '''else''' return[<tex>v</tex>] > enter[<tex>u</tex>]
                     <tex>return[v] \leftarrow return[u]</tex>
+
                     return[<tex>v</tex>] <tex>\leftarrow</tex> return[<tex>u</tex>]
     start()
+
     ...
        для всех <tex>v</tex> вершин графа:
+
    '''for''' <tex> v \in V</tex>:
            если (<tex>v</tex> не посещена)
+
        '''if''' '''not''' visited[<tex>v</tex>]
                <tex>time \leftarrow 0</tex>
+
            time <tex>\leftarrow</tex> 0
                dfs(<tex>v</tex>, -1)
+
            <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

Двупроходный алгоритм

Найти компоненты вершинной двусвязности неориентированного графа можно с помощью обхода в глубину.

Первый проход Используем первый проход, чтобы найти точки сочленения.

Второй проход Точка сочленения принадлежит как минимум двум компонентам вершинной двусвязности. Вершина [math] v \ne root [/math] является точкой сочленения, если у нее есть сын [math] u : return[u] \geqslant enter[v] [/math].
Это также значит, что ребро [math] vu [/math] содержится в другой компоненте вершинной двусвязности, нежели ребро по которому мы пришли в вершину [math] v [/math] , используя поиск в глубину.
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.

Псевдокод второго прохода

  function [math]dfs[/math]([math]v[/math], color, parent):
      for [math] u \in V : (v, u) \in E[/math]:
           if [math]u[/math] == parent
               continue
           if not visited[[math]u[/math]]
               if return[[math]u[/math]] [math]\geqslant[/math] enter[[math]v[/math]]
                   newColor [math]\leftarrow[/math] maxColor++
                   col[[math]vu[/math]] [math]\leftarrow[/math] newColor
                   [math]dfs[/math]([math]u[/math], newColor, [math]v[/math])
               else
                   col[[math]vu[/math]] [math]\leftarrow[/math] color
                   [math]dfs[/math]([math]u[/math], color, [math]v[/math])
           else
               if enter[[math]u[/math]] [math]\leqslant[/math] enter[[math]v[/math]]
                   col[[math]vu[/math]] [math]\leftarrow[/math] color
   ...
   for [math] v \in V[/math]:
       if not visited[[math]v[/math]]
           [math]dfs[/math]([math]v[/math], -1, -1)
Компоненты обозначены разным цветом


Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.
В алгоритме выполняется два прохода [math]dfs[/math], каждый из которых работает [math]O(V + E)[/math]. Значит время работы алгоритма [math]O(V + E)[/math].

Однопроходный алгоритм

Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека.
Таким образом, каждый раз находя компоненту вершинной двусвязности мы сможем покрасить все ребра, содержащиеся в ней, в новый цвет.

Доказательство корректности алгоритма

Предположим, что граф содержит точку сочленения [math] i' \in V [/math] , за которой следует один или несколько блоков. Вершины из этих блоков образуют подмножество [math] V' \subset V [/math]. В таком случае:

  1. Все вершины [math] V' [/math] являются потомками [math] i' [/math] в дереве обхода;
  2. Все вершины [math] V' [/math] будут пройдены в течение периода серого состояния [math] i' [/math];
  3. В [math] G [/math] не может быть обратных дуг из [math] V' [/math] в [math] V \setminus V' [/math].

Значит все дуги [math] V' [/math] будут будут добавлены в стек после дуги ведущей из точки сочленения в блок. В стеке в момент обнаружения точки сочленения будут находиться только дуги блока, связанного с ней, т.к. блоки найденные до него (если таковые имеются) будет уже извлечены из стека и покрашены в свой цвет.

Псевдокод

   function [math]dfs[/math]([math]v[/math], parent):
       enter[[math]v[/math]] [math]\leftarrow[/math] return[[math]v[/math]] [math]\leftarrow[/math] time++
       for [math] u \in V : (v, u) \in E[/math]:
           if [math]u[/math] == parent 
               continue
           if not visited[[math]u[/math]]
               stack.push([math]vu[/math])
               [math]dfs[/math]([math]u, v[/math])
               if return[[math]u[/math]] [math]\geqslant[/math] enter[[math]v[/math]]
                   color [math]\leftarrow [/math] maxColor++
                   while stack.top() != [math]vu[/math]
                       colors[stack.top()] [math] \leftarrow [/math] color
                       stack.pop()
                   colors[[math]vu[/math]] [math]\leftarrow [/math] color
                   stack.pop()
               if return[[math]u[/math]] < return[[math]v[/math]]
                   return[[math]v[/math]] [math]\leftarrow[/math] return[[math]u[/math]]
           else
               if enter[[math]u[/math]] < enter[[math]v[/math]] 
                   stack.push([math]vu[/math])
               else return[[math]v[/math]] > enter[[math]u[/math]]
                   return[[math]v[/math]] [math]\leftarrow[/math] return[[math]u[/math]]
   ...
   for [math] v \in V[/math]:
       if not visited[[math]v[/math]]
           time [math]\leftarrow[/math] 0
           [math]dfs[/math]([math]v[/math], -1)


Во время алгоритма совершается один проход [math]dfs[/math], который работает за [math]O(V + E)[/math]. Внутри него совершается еще цикл, который суммарно выполняет [math]O(E)[/math] операций, т.к. каждое ребро может быть добавлено в стек только один раз. Следовательно, общее время работы алгоритма [math]O(V + E) + O(E) = O(V + E)[/math]

Литература

  • В.А.Кузнецов, А.М.Караваев. "Оптимизация на графах" - Петрозаводск, Издательство ПетрГУ 2007