encompass-cs/src/graph/DirectedGraph.cs

231 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
public enum SearchSymbol
{
start,
finish
}
public class DirectedGraph<T>
{
protected List<T> _vertices = new List<T>();
protected Dictionary<T, List<T>> _edges = new Dictionary<T, List<T>>();
public IEnumerable<T> Vertices { get { return _vertices; } }
/*
* GRAPH STRUCTURE METHODS
*/
public void AddVertex(T vertex)
{
if (!VertexExists(vertex))
{
_vertices.Add(vertex);
_edges.Add(vertex, new List<T>());
}
}
public void AddVertices(params T[] vertices)
{
foreach (var vertex in vertices)
{
AddVertex(vertex);
}
}
public bool VertexExists(T vertex)
{
return Vertices.Contains(vertex);
}
public void RemoveVertex(T vertex)
{
if (VertexExists(vertex))
{
foreach (var u in Neighbors(vertex))
{
RemoveEdge(vertex, u);
}
_vertices.Remove(vertex);
}
}
public void AddEdge(T v, T u)
{
if (VertexExists(v) && VertexExists(u))
{
_edges[v].Add(u);
}
}
public void AddEdges(params Tuple<T, T>[] edges)
{
foreach (var edge in edges)
{
AddEdge(edge.Item1, edge.Item2);
}
}
public void RemoveEdge(T v, T u)
{
_edges[v].Remove(u);
}
public IEnumerable<T> Neighbors(T vertex)
{
if (VertexExists(vertex))
{
return _edges[vertex];
}
else
{
return Enumerable.Empty<T>();
}
}
/*
* GRAPH ANALYSIS METHODS
*/
public Dictionary<T, Dictionary<SearchSymbol, uint>> NodeDFS()
{
var discovered = new HashSet<T>();
uint time = 0;
var output = new Dictionary<T, Dictionary<SearchSymbol, uint>>();
foreach (var vertex in Vertices)
{
output.Add(vertex, new Dictionary<SearchSymbol, uint>());
}
Action<T> dfsHelper = null;
dfsHelper = (T v) =>
{
discovered.Add(v);
time += 1;
output[v].Add(SearchSymbol.start, time);
foreach (var neighbor in Neighbors(v))
{
if (!discovered.Contains(neighbor))
{
dfsHelper(neighbor);
}
}
time += 1;
output[v].Add(SearchSymbol.finish, time);
};
foreach (var vertex in Vertices)
{
if (!discovered.Contains(vertex))
{
dfsHelper(vertex);
}
}
return output;
}
public IEnumerable<T> TopologicalSort()
{
var dfs = NodeDFS();
var priority = new SortedList<uint, T>();
foreach (var entry in dfs)
{
priority.Add(entry.Value[SearchSymbol.finish], entry.Key);
}
return priority.Values.Reverse();
}
public IEnumerable<IEnumerable<T>> StronglyConnectedComponents()
{
var preorder = new Dictionary<T, uint>();
var lowlink = new Dictionary<T, uint>();
var sccFound = new Dictionary<T, bool>();
var sccQueue = new Stack<T>();
var result = new List<List<T>>();
uint preorderCounter = 0;
foreach (var source in Vertices)
{
if (!sccFound.ContainsKey(source))
{
var queue = new Stack<T>();
queue.Push(source);
while (queue.Count > 0)
{
var v = queue.Peek();
if (!preorder.ContainsKey(v))
{
preorderCounter += 1;
preorder[v] = preorderCounter;
}
var done = true;
var vNeighbors = Neighbors(v);
foreach (var w in vNeighbors)
{
if (!preorder.ContainsKey(w))
{
queue.Push(w);
done = false;
break;
}
}
if (done)
{
lowlink[v] = preorder[v];
foreach (var w in vNeighbors)
{
if (!sccFound.ContainsKey(w))
{
if (preorder[w] > preorder[v])
{
lowlink[v] = Math.Min(lowlink[v], lowlink[w]);
}
else
{
lowlink[v] = Math.Min(lowlink[v], preorder[w]);
}
}
}
queue.Pop();
if (lowlink[v] == preorder[v])
{
sccFound[v] = true;
var scc = new List<T>();
scc.Add(v);
while (sccQueue.Count > 0 && preorder[sccQueue.Peek()] > preorder[v])
{
var k = sccQueue.Pop();
sccFound[k] = true;
scc.Add(k);
}
result.Add(scc);
}
else
{
sccQueue.Push(v);
}
}
}
}
}
return result;
}
}
}