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

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

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

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

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

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

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

function [math]dfs[/math]([math]v[/math], color, parent):
  for [math] u : (v, u) \in E[/math]:
    if [math]u[/math] == parent
      continue
    if not visited[[math]u[/math]]
      if up[[math]u[/math]] [math]\geqslant[/math] tin[[math]v[/math]]
        newColor = maxColor++
        col[[math]vu[/math]] = newColor
        dfs([math]u[/math], newColor, [math]v[/math])
      else
        col[[math]vu[/math]] = color
        dfs([math]u[/math], color, [math]v[/math])
    else if up[[math]u[/math]] [math]\leqslant[/math] tin[[math]v[/math]]
      col[[math]vu[/math]] = color

for [math] v \in V[/math]:
  if not visited[[math]v[/math]]
    dfs([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):
  tin[[math]v[/math]] = up[[math]v[/math]] = time++
  for [math] u : (v, u) \in E[/math]:
    if [math]u[/math] == parent 
      continue
    if not visited[[math]u[/math]]
      stack.push([math]vu[/math])
      dfs([math]u, v[/math])
      if up[[math]u[/math]] [math]\geqslant[/math] tin[[math]v[/math]]
        color = maxColor++
        while stack.top() != ([math]vu[/math])
          colors[stack.top()] = color
          stack.pop()
        colors[[math]vu[/math]] =  color
        stack.pop()
      if up[[math]u[/math]] < up[[math]v[/math]]
        up[[math]v[/math]] = up[[math]u[/math]]
    else if tin[[math]u[/math]] < tin[[math]v[/math]] 
      stack.push([math]vu[/math])
    else if up[[math]v[/math]] > tin[[math]u[/math]]
      up[[math]v[/math]] = up[[math]u[/math]]
for [math] v \in V[/math]:
  if not visited[[math]v[/math]]
    time = 0
    dfs([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