c# – 循环枚举具有多边的有向图
|
如何在多边形的有向图中找到所有的圆形?
图示例1: 周期: 1-2-6 1-2-3-4 1-2-3-4-5-6 1-2-6-5-3-4 3-4-5 5-6 图示例2(多边4/5): 周期: 1-2-3 1-4 1-5 笔记: 我不想检测一个循环(布尔结果),我想列出所有循环. 任何Strongly connected component算法都不足以满足我的问题(在这两个例子中只能找到一个组件). 我在C#中使用QuickGraph实现,但我很乐意看到任何语言的算法. 解决方法
我有这个问题的乐趣,谢谢! :P
我在C#中有一个解决方案.找到周期的算法非常短(约10行),但是它周围有很多杂乱(例如类和节点类的实现). 我使用变量命名约定,字母“e”表示一个边,字母“a”是边缘开始的节点,“b”表示它链接到的节点.通过这些惯例,这是算法: public static IEnumerable<Cycle> FindAllCycles()
{
HashSet<Node> alreadyVisited = new HashSet<Node>();
alreadyVisited.Add(Node.AllNodes[0]);
return FindAllCycles(alreadyVisited,Node.AllNodes[0]);
}
private static IEnumerable<Cycle> FindAllCycles(HashSet<Node> alreadyVisited,Node a)
{
for (int i = 0; i < a.Edges.Count; i++)
{
Edge e = a.Edges[i];
if (alreadyVisited.Contains(e.B))
{
yield return new Cycle(e);
}
else
{
HashSet<Node> newSet = i == a.Edges.Count - 1 ? alreadyVisited : new HashSet<Node>(alreadyVisited);
newSet.Add(e.B);
foreach (Cycle c in FindAllCycles(newSet,e.B))
{
c.Build(e);
yield return c;
}
}
}
}
它有一个优化来重用一些Hashsets,这可能会令人困惑.我已经包括以下代码,它产生完全相同的结果,但是这个实现没有优化,所以你可以更容易地找出它的工作原理. private static IEnumerable<Cycle> FindAllCyclesUnoptimized(HashSet<Node> alreadyVisited,Node a)
{
foreach (Edge e in a.Edges)
if (alreadyVisited.Contains(e.B))
yield return new Cycle(e);
else
{
HashSet<Node> newSet = new HashSet<Node>(alreadyVisited);
newSet.Add(e.B);//EDIT: thnx dhsto
foreach (Cycle c in FindAllCyclesUnoptimized(newSet,e.B))
{
c.Build(e);
yield return c;
}
}
}
这使用Node,Edge和Cycle的以下实现.他们很简单,尽管我做了很多想法,使一切都不变,成员尽可能少的访问. public sealed class Node
{
public static readonly ReadOnlyCollection<Node> AllNodes;
internal static readonly List<Node> allNodes;
static Node()
{
allNodes = new List<Node>();
AllNodes = new ReadOnlyCollection<Node>(allNodes);
}
public static void SetReferences()
{//call this method after all nodes have been created
foreach (Edge e in Edge.AllEdges)
e.A.edge.Add(e);
}
//All edges linking *from* this node,not to it.
//The variablename "Edges" it quite unsatisfactory,but I couldn't come up with anything better.
public ReadOnlyCollection<Edge> Edges { get; private set; }
internal List<Edge> edge;
public int Index { get; private set; }
public Node(params int[] nodesIndicesConnectedTo)
{
this.edge = new List<Edge>(nodesIndicesConnectedTo.Length);
this.Edges = new ReadOnlyCollection<Edge>(edge);
this.Index = allNodes.Count;
allNodes.Add(this);
foreach (int nodeIndex in nodesIndicesConnectedTo)
new Edge(this,nodeIndex);
}
public override string ToString()
{
return this.Index.ToString();
}
}
public sealed class Edge
{
public static readonly ReadOnlyCollection<Edge> AllEdges;
static readonly List<Edge> allEdges;
static Edge()
{
allEdges = new List<Edge>();
AllEdges = new ReadOnlyCollection<Edge>(allEdges);
}
public int Index { get; private set; }
public Node A { get; private set; }
public Node B { get { return Node.allNodes[this.bIndex]; } }
private readonly int bIndex;
internal Edge(Node a,int bIndex)
{
this.Index = allEdges.Count;
this.A = a;
this.bIndex = bIndex;
allEdges.Add(this);
}
public override string ToString()
{
return this.Index.ToString();
}
}
public sealed class Cycle
{
public readonly ReadOnlyCollection<Edge> Members;
private List<Edge> members;
private bool IsComplete;
internal void Build(Edge member)
{
if (!IsComplete)
{
this.IsComplete = member.A == members[0].B;
this.members.Add(member);
}
}
internal Cycle(Edge firstMember)
{
this.members = new List<Edge>();
this.members.Add(firstMember);
this.Members = new ReadOnlyCollection<Edge>(this.members);
}
public override string ToString()
{
StringBuilder result = new StringBuilder();
foreach (var member in this.members)
{
result.Append(member.Index.ToString());
if (member != members[members.Count - 1])
result.Append(",");
}
return result.ToString();
}
}
然后为了说明如何使用这个小型API,我已经实现了你的两个例子. static void Main(string[] args)
{
InitializeExampleGraph1();//or: InitializeExampleGraph2();
Node.SetReferences();
var allCycles = FindAllCycles().ToList();
}
static void InitializeExampleGraph1()
{
new Node(1,2);//says that the first node(node a) links to b and c.
new Node(2);//says that the second node(node b) links to c.
new Node(0,3);//says that the third node(node c) links to a and d.
new Node(0);//etc
}
static void InitializeExampleGraph2()
{
new Node(1);
new Node(0,2);
new Node(0);
}
我必须注意,这些例子中边缘的索引不符合图像中的索引,但是通过简单的查找可以避免这些索引. {3,2,0}
{5,4,0}
{3,1}
{5,1}
allCycles是第二个例子: {1,0}
{2,0}
{4,3,0}
我希望您对此解决方案感到满意,并且您使用它.我几乎没有评论代码,所以我知道这可能很难理解.在这种情况下,请问,我会对此发表评论! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
