-
Notifications
You must be signed in to change notification settings - Fork 294
/
Copy pathTarjansArticulationFinder.cs
82 lines (71 loc) · 3.25 KB
/
TarjansArticulationFinder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using System;
using System.Collections.Generic;
using Advanced.Algorithms.DataStructures.Graph;
namespace Advanced.Algorithms.Graph;
/// <summary>
/// Articulation point finder using Tarjan's algorithm.
/// </summary>
public class TarjansArticulationFinder<T>
{
/// <summary>
/// Returns a list if articulation points in this graph.
/// </summary>
public List<T> FindArticulationPoints(IGraph<T> graph)
{
var visitTime = 0;
return Dfs(graph.ReferenceVertex, new List<T>(),
new Dictionary<T, int>(), new Dictionary<T, int>(),
new Dictionary<T, T>(),
ref visitTime);
}
/// <summary>
/// Do a depth first search to find articulation points by keeping track of
/// discovery nodes and checking for back edges using low/discovery time maps.
/// </summary>
private List<T> Dfs(IGraphVertex<T> currentVertex,
List<T> result,
Dictionary<T, int> discoveryTimeMap, Dictionary<T, int> lowTimeMap,
Dictionary<T, T> parent, ref int discoveryTime)
{
var isArticulationPoint = false;
discoveryTimeMap.Add(currentVertex.Key, discoveryTime);
lowTimeMap.Add(currentVertex.Key, discoveryTime);
//discovery childs in this iteration
var discoveryChildCount = 0;
foreach (var edge in currentVertex.Edges)
if (!discoveryTimeMap.ContainsKey(edge.TargetVertexKey))
{
discoveryChildCount++;
parent.Add(edge.TargetVertexKey, currentVertex.Key);
discoveryTime++;
Dfs(edge.TargetVertex, result,
discoveryTimeMap, lowTimeMap, parent, ref discoveryTime);
//if neighbours lowTime is greater than current
//then this is an articulation point
//because neighbour never had a chance to propogate any ancestors low value
//since this is an isolated componant
if (discoveryTimeMap[currentVertex.Key] <= lowTimeMap[edge.TargetVertexKey])
isArticulationPoint = true;
else
//propogate lowTime index of neighbour so that ancestors can see it in DFS
lowTimeMap[currentVertex.Key] =
Math.Min(lowTimeMap[currentVertex.Key], lowTimeMap[edge.TargetVertexKey]);
}
else
{
//check if this edge target vertex is not in the current DFS path
//even if edge target vertex was already visisted
//update this so that ancestors can see it
if (parent.ContainsKey(currentVertex.Key) == false
|| !edge.TargetVertexKey.Equals(parent[currentVertex.Key]))
lowTimeMap[currentVertex.Key] =
Math.Min(lowTimeMap[currentVertex.Key], discoveryTimeMap[edge.TargetVertexKey]);
}
//if root of DFS with two or more children
//or visitTime of this Vertex <=lowTime of any neighbour
if (parent.ContainsKey(currentVertex.Key) == false && discoveryChildCount >= 2 ||
parent.ContainsKey(currentVertex.Key) && isArticulationPoint)
result.Add(currentVertex.Key);
return result;
}
}