<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Andreikkaa</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Andreikkaa"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Andreikkaa"/>
		<updated>2026-05-23T05:56:25Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B0_%D0%B2_%D0%B3%D0%BB%D1%83%D0%B1%D0%B8%D0%BD%D1%83_%D0%B4%D0%BB%D1%8F_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D1%82%D0%BE%D1%87%D0%B5%D0%BA_%D1%81%D0%BE%D1%87%D0%BB%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F&amp;diff=64271</id>
		<title>Использование обхода в глубину для поиска точек сочленения</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B0_%D0%B2_%D0%B3%D0%BB%D1%83%D0%B1%D0%B8%D0%BD%D1%83_%D0%B4%D0%BB%D1%8F_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D1%82%D0%BE%D1%87%D0%B5%D0%BA_%D1%81%D0%BE%D1%87%D0%BB%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F&amp;diff=64271"/>
				<updated>2018-03-13T16:06:46Z</updated>
		
		<summary type="html">&lt;p&gt;Andreikkaa: Неправильная обработка вершины-корня дерева dfs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Задача&lt;br /&gt;
|definition=Дан [[Отношение связности, компоненты связности|связный]] [[Основные определения теории графов|неориентированный граф]] &amp;lt;tex&amp;gt; G &amp;lt;/tex&amp;gt;. Найти все [[Точка сочленения, эквивалентные определения|точки сочленения]] в &amp;lt;tex&amp;gt; G &amp;lt;/tex&amp;gt; за время &amp;lt;tex&amp;gt; O(|V| + |E|).&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
=== Описание алгоритма ===&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из произвольной вершины графа; обозначим её через &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt;. Заметим следующий факт:&lt;br /&gt;
&lt;br /&gt;
* Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Тогда, если текущее ребро (&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;to&amp;lt;/tex&amp;gt;) таково, что из вершины &amp;lt;tex&amp;gt;to&amp;lt;/tex&amp;gt; и из любого её потомка в дереве обхода в глубину нет обратного ребра в вершину &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; или какого-либо её предка, то это ребро является мостом. В противном случае оно мостом не является. (В самом деле, мы этим условием проверяем, нет ли другого пути из &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;to&amp;lt;/tex&amp;gt;, кроме как спуск по ребру (&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;to&amp;lt;/tex&amp;gt;) дерева обхода в глубину.)&lt;br /&gt;
&lt;br /&gt;
Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся &amp;quot;временами входа в вершину&amp;quot;, вычисляемыми алгоритмом поиска в глубину.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Joint_point_2_rsz.png‎|280px|thumb|left| &amp;lt;font color=red&amp;gt;Красным&amp;lt;/font&amp;gt; цветом обозначены точки сочленения&amp;lt;br&amp;gt;&amp;lt;font color=blue&amp;gt;Синим&amp;lt;/font&amp;gt; — ребра по которым идет DFS]]&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;tin[u]&amp;lt;/tex&amp;gt; — время входа поиска в глубину в вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;. Через &amp;lt;tex&amp;gt;up[u]&amp;lt;/tex&amp;gt; обозначим минимум из времени захода в саму вершину &amp;lt;tex&amp;gt;tin[u]&amp;lt;/tex&amp;gt;, времен захода в каждую из вершин &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;, являющуюся концом некоторого обратного ребра &amp;lt;tex&amp;gt;(u,p)&amp;lt;/tex&amp;gt;, а также из всех значений &amp;lt;tex&amp;gt;up[v]&amp;lt;/tex&amp;gt; для каждой вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, являющейся непосредственным сыном &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в дереве поиска.&lt;br /&gt;
&lt;br /&gt;
Тогда из вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; или её потомка есть обратное ребро в её предка &amp;lt;tex&amp;gt;\Leftrightarrow \exists&amp;lt;/tex&amp;gt; такой сын &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;up[v] \geqslant tin[u]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, если для текущей вершины &amp;lt;tex&amp;gt;v \ne root &amp;lt;/tex&amp;gt; существует непосредственный сын &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;: &amp;lt;tex&amp;gt;up[v] \geqslant tin[u]&amp;lt;/tex&amp;gt;, то вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; является точкой сочленения, в противном случае она точкой сочленения не является.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
 &lt;br /&gt;
 '''function''' findCutPoints(G[n]: '''Graph'''):&amp;lt;font color=darkgreen&amp;gt; // функция принимает граф G с количеством вершин n и выполняет поиск точек сочленения во всем графе &amp;lt;/font&amp;gt;&lt;br /&gt;
     visited = array[n, ''false'']&lt;br /&gt;
                    &lt;br /&gt;
    '''function''' dfs(v: '''int''', p: '''int'''):&lt;br /&gt;
       time = time + 1&lt;br /&gt;
       up[v] = tin[v] = time &lt;br /&gt;
       visited[v] = ''true''&lt;br /&gt;
       count = 0             &lt;br /&gt;
       '''for''' u: (v, u) '''in''' G   &lt;br /&gt;
          '''if''' u == p&lt;br /&gt;
             '''continue'''&lt;br /&gt;
          '''if''' visited[u]&lt;br /&gt;
             up[v] = min(up[v], tin[u])&lt;br /&gt;
          '''else'''&lt;br /&gt;
             dfs(u, v) &lt;br /&gt;
             count = count + 1&lt;br /&gt;
             up[v] = min(up[v], up[u])&lt;br /&gt;
             '''if''' p != -1 '''and''' up[u] &amp;gt;= tin[v]&lt;br /&gt;
                  v — cutpoint &lt;br /&gt;
       '''if''' p == -1 '''and''' count &amp;gt;= 2&lt;br /&gt;
             v — cutpoint &lt;br /&gt;
                    	   &lt;br /&gt;
    '''for''' i = 1 '''to''' n             &lt;br /&gt;
       '''if''' '''not''' visited[i]              &lt;br /&gt;
          dfs(i, -1)&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; — дерево [[Обход в глубину, цвета вершин|обхода в глубину]], &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; — корень &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* Вершина &amp;lt;tex&amp;gt;u \ne root&amp;lt;/tex&amp;gt; — точка сочленения &amp;lt;tex&amp;gt;\Leftrightarrow \exists v \in T&amp;lt;/tex&amp;gt; — сын &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; : из &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; или любого потомка вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; нет обратного ребра в предка вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; — точка сочленения &amp;lt;tex&amp;gt;\Leftrightarrow root&amp;lt;/tex&amp;gt; имеет хотя бы двух сыновей в дереве поиска в глубину.&lt;br /&gt;
|proof=&lt;br /&gt;
[[Файл:Joint_point_1.png|48px |thumb|‎ | Рисунок к &amp;lt;tex&amp;gt;\Leftarrow&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&amp;lt;tex&amp;gt;\Leftarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#Удалим &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Докажем, что не существует пути из &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; в любого предка вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;. Пусть это не так. Тогда &amp;lt;tex&amp;gt;\exists x \in T&amp;lt;/tex&amp;gt; — предок &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;\exists&amp;lt;/tex&amp;gt; путь из &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;G \backslash u&amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; — предпоследняя вершина на этом пути, &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; — потомок &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;(w, x)&amp;lt;/tex&amp;gt; — не ребро дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;(в силу единственности пути в дереве) &amp;lt;tex&amp;gt;\Rightarrow (w, x)&amp;lt;/tex&amp;gt; — обратное ребро, что противоречит условию.&lt;br /&gt;
#Пусть у &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; хотя бы два сына. Тогда при удалении &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; не существует пути между его поддеревьями, так как не существует перекрестных ребер &amp;lt;tex&amp;gt;\Rightarrow root&amp;lt;/tex&amp;gt; — точка сочленения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\Rightarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
#Докажем что из отрицания второго утверждения следует отрицание первого. Обозначим через &amp;lt;tex&amp;gt;G'&amp;lt;/tex&amp;gt; граф, состоящий из вершин, не являющихся потомками &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;. Удалим вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;. Очевидно, что граф &amp;lt;tex&amp;gt;G'&amp;lt;/tex&amp;gt; и все поддеревья вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; останутся связными, кроме того из каждого поддерева есть ребро в &amp;lt;tex&amp;gt;G' \Rightarrow G \backslash u&amp;lt;/tex&amp;gt; — связный &amp;lt;tex&amp;gt;\Rightarrow u&amp;lt;/tex&amp;gt; — не точка сочленения.&lt;br /&gt;
#Пусть &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; — точка сочленения и у него есть только один сын. Тогда при удалении &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; остается дерево с корнем в его сыне, содержащее все остальные вершины графа, то есть оставшийся граф связен — противоречие с тем, что &amp;lt;tex&amp;gt;root&amp;lt;/tex&amp;gt; — точка сочленения.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Асимптотика ===&lt;br /&gt;
Оценим время работы алгоритма. Процедура &amp;lt;tex&amp;gt;\mathrm{dfs}&amp;lt;/tex&amp;gt; вызывается от каждой вершины не более одного раза, а внутри процедуры рассматриваются все такие [[Основные определения теории графов|ребра]] &amp;lt;tex&amp;gt;\{e\ |\ \mathrm{begin(e)} = v\}&amp;lt;/tex&amp;gt;. Всего таких ребер для всех вершин в графе &amp;lt;tex&amp;gt;O(E)&amp;lt;/tex&amp;gt;, следовательно, время работы алгоритма оценивается как &amp;lt;tex&amp;gt;O(V+E)&amp;lt;/tex&amp;gt;. Такое же, как у [[Обход в глубину, цвета вершин|обхода в глубину]].&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Использование обхода в глубину для поиска мостов]]&lt;br /&gt;
* [[Обход в глубину, цвета вершин]]&lt;br /&gt;
* [[Обход в ширину]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* Асанов М., Баранский В., Расин В. — Дискретная математика: Графы, матроиды, алгоритмы — Лань, 2010. — 368 с. — ISBN 978-5-8114-1068-2&lt;br /&gt;
* [http://e-maxx.ru/algo/cutpoints MAXimal :: algo :: Поиск точек сочленения]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Обход в глубину]]&lt;/div&gt;</summary>
		<author><name>Andreikkaa</name></author>	</entry>

	</feed>