在给定源顶点的情况下,在有向图中查找具有循环的所有路径

时间:2012-01-16 20:06:20

标签: algorithm language-agnostic graph

我无法解决这个问题。我必须从有向图中包含简单循环的源顶点 s 开始找到所有简单路径。即,不允许重复,当然除了循环连接回路径的单个重复顶点。

我知道如何使用DFS访问来查找图表是否有周期,但我无法找到一种方法来使用它来查找从 s 开始的所有此类路径。

例如,在此图中

        +->B-+
        |    v
s-->T-->A<---C
        |    ^
        +->D-+

s开始,将正确找到路径S-T-A-B-C-A。但是找不到路径S-T-A-D-C-A,因为顶点C被标记为DFS访问。

有人可以提示我如何解决这个问题吗? 感谢

4 个答案:

答案 0 :(得分:6)

这实际上是一个非常简单的算法,比DFS简单。您只需在一个简单的递归搜索中枚举所有路径,记住在路径循环回来的任何时候都不再递归:

(这只是一个Python启发的伪代码。我希望它足够清楚。)

 def find_paths_with_cycles(path_so_far):
       node_just_added = path_so_far.back()
       for neigh in out_neighbours(node_just_added):
           if neigh in path_so_far:
               # this is a cycle, just print it
               print path_so_far + [neigh]
           else:
               find_paths_with_cycles(path_so_far + [neigh])


 initial_path = list()
 initial_path.append(s)
 find_paths_with_cycles(initial_path)

答案 1 :(得分:0)

这是垃圾收集算法的常见问题。

在.net培训中,我了解到.net垃圾收集器通过从图中的两个指针开始检测周期,其中一个指针的速度是另一个指针的两倍。如果快速前进的一个从后面进入慢速,你发现了一个循环。它将更复杂地用于复杂的图形,但它可以在不标记节点的情况下工作。

答案 2 :(得分:0)

当您找到一个循环时,返回并在缩回过去时取消标记顶点。

假设您找到了SABCA并希望找到下一个周期。 A是您的最终节点,您不应该取消标记。回到C. C还有另一个优势吗?不,所以取消标记C 并返回B.是否还有另一条边缘走出B?不,取消标记B并返回A.是否有另一个边缘走出A?是!有一个去D.所以去那里,标记D,去C 现在没有标记,然后到A.在这里,你找到了另一个循环。你再次缩回A,但现在没有更多的路径从A出来,所以你取消标记A并返回S。

答案 3 :(得分:0)

我继续用C#实现了Aaron的算法。

因为它使用延迟枚举的IEnumerable,你可以使用DirectedGraphHelper.FindSimpleCycles(s).First()如果你只想要找到第一个循环:

public static class DirectedGraphHelper
{
    public static IEnumerable<Node[]> FindSimpleCycles(Node startNode)
    {
        return FindSimpleCyclesCore(new Stack<Node>(new[] { startNode }));
    }

    private static IEnumerable<Node[]> FindSimpleCyclesCore(Stack<Node> pathSoFar)
    {
        var nodeJustAdded = pathSoFar.Peek();
        foreach (var target in nodeJustAdded.Targets)
        {
            if (pathSoFar.Contains(target))
            {
                yield return pathSoFar.Reverse().Concat(new[] { target }).ToArray();
            }
            else
            {
                pathSoFar.Push(target);
                foreach (var simpleCycle in FindSimpleCyclesCore(pathSoFar))
                {
                    yield return simpleCycle;
                }
                pathSoFar.Pop();
            }
        }
    }
}

public class Node
{
    public string Id { get; private set; }
    public readonly List<Node> Targets = new List<Node>();

    public Node(string id)
    {
        this.Id = id;
    }
}

你会像这样使用它:

class Program
{
    static void Main(string[] args)
    {
        var s = new Node("s");
        var t = new Node("t");
        var a = new Node("a");
        var b = new Node("b");
        var c = new Node("c");
        var d = new Node("d");

        s.Targets.Add(t);
        t.Targets.Add(a);
        a.Targets.AddRange(new[] { b, d });
        b.Targets.Add(c);
        c.Targets.Add(a);
        d.Targets.Add(c);

        foreach (var cycle in DirectedGraphHelper.FindSimpleCycles(s))
        {
            Console.WriteLine(string.Join(",", cycle.Select(n => n.Id)));
        }
        Console.Read();
    }
}