我有 Tarjan 算法的以下(递归)实现,可以在图中找到强连接的组件,它工作正常:
public class StronglyConnectedComponents
{
public static List<List<int>> Search(Graph graph)
{
StronglyConnectedComponents scc = new StronglyConnectedComponents();
return scc.Tarjan(graph);
}
private int preCount;
private int[] low;
private bool[] visited;
private Graph graph;
private List<List<int>> stronglyConnectedComponents = new List<List<int>>();
private Stack<int> stack = new Stack<int>();
public List<List<int>> Tarjan(Graph graph)
{
this.graph = graph;
low = new int[graph.VertexCount];
visited = new bool[graph.VertexCount];
for (int v = 0; v < graph.VertexCount; v++) if (!visited[v]) DFS(v);
return stronglyConnectedComponents;
}
public void DFS(int v)
{
low[v] = preCount++;
visited[v] = true;
stack.Push(v);
int min = low[v];
int edgeCount = graph.OutgoingEdgeCount(v);
for (int i = 0; i < edgeCount; i++)
{
var edge = graph.OutgoingEdge(v, i);
int target = edge.Target;
if (!visited[target]) DFS(target);
if (low[target] < min) min = low[target];
}
if (min < low[v])
{
low[v] = min;
return;
}
List<int> component = new List<int>();
int w;
do
{
w = stack.Pop();
component.Add(w);
low[w] = graph.VertexCount;
} while (w != v);
stronglyConnectedComponents.Add(component);
}
}
但是在大图上,显然,递归版本会抛出 StackOverflowException。因此我想让算法非递归。
我试图DFS
用以下(非递归)函数替换该函数,但该算法不再起作用。有人可以帮忙吗?
private void DFS2(int vertex)
{
bool[] visited = new bool[graph.VertexCount];
Stack<int> stack = new Stack<int>();
stack.Push(vertex);
int min = low[vertex];
while (stack.Count > 0)
{
int v = stack.Pop();
if (visited[v]) continue;
visited[v] = true;
int edgeCount = graph.OutgoingEdgeCount(v);
for (int i = 0; i < edgeCount; i++)
{
int target = graph.OutgoingEdge(v, i).Target;
stack.Push(target);
if (low[target] < min) min = low[target];
}
}
if (min < low[vertex])
{
low[vertex] = min;
return;
}
List<int> component = new List<int>();
int w;
do
{
w = stack.Pop();
component.Add(w);
low[w] = graph.VertexCount;
} while (w != vertex);
stronglyConnectedComponents.Add(component);
}
以下代码显示了测试:
public void CanFindStronglyConnectedComponents()
{
Graph graph = new Graph(8);
graph.AddEdge(0, 1);
graph.AddEdge(1, 2);
graph.AddEdge(2, 3);
graph.AddEdge(3, 2);
graph.AddEdge(3, 7);
graph.AddEdge(7, 3);
graph.AddEdge(2, 6);
graph.AddEdge(7, 6);
graph.AddEdge(5, 6);
graph.AddEdge(6, 5);
graph.AddEdge(1, 5);
graph.AddEdge(4, 5);
graph.AddEdge(4, 0);
graph.AddEdge(1, 4);
var scc = StronglyConnectedComponents.Search(graph);
Assert.AreEqual(3, scc.Count);
Assert.IsTrue(SetsEqual(Set(5, 6), scc[0]));
Assert.IsTrue(SetsEqual(Set(7, 3, 2), scc[1]));
Assert.IsTrue(SetsEqual(Set(4, 1, 0), scc[2]));
}
private IEnumerable<int> Set(params int[] set) => set;
private bool SetsEqual(IEnumerable<int> set1, IEnumerable<int> set2)
{
if (set1.Count() != set2.Count()) return false;
return set1.Intersect(set2).Count() == set1.Count();
}