簡體   English   中英

通過無向無環圖查找所有路徑

[英]Find all paths through an undirected, acyclic graph

我試圖通過下圖找到所有路徑:

    start
    /   \
c--A-----b--d
    \   /
     end

它只是非循環的,因為小寫名稱的頂點只能被訪問一次,例如頂點d不應該被訪問,因為要將它包含在通過圖的路徑中,頂點b必須被訪問兩次。

通過圖表的所有允許路徑是:

start,A,b,A,c,A,end
start,A,b,A,end
start,A,b,end
start,A,c,A,b,A,end
start,A,c,A,b,end
start,A,c,A,end
start,A,end
start,b,A,c,A,end
start,b,A,end
start,b,end

幾乎我所有的谷歌結果都是針對有向圖的,我能找到讓它們適用於我的無向圖的唯一方法是將每條邊添加兩次,每個方向一次,我認為這比我目前的不工作更混亂解決方案。

我想出的最好的方法是通過圖形進行遞歸遍歷:

private HashSet<List<Edge>> paths = new();
private List<Edge> _path = new();
HashSet<Vertex> smallsVisited = new HashSet<Vertex>();
private void Traverse(Vertex start)
{
    var traverseEnd = GetVertexByName("end");
    if (start == traverseEnd)
    {
        paths.Add(_path.ToList());
        _path.Clear();
        return;
    }

    if (smallsVisited.Contains(start))
        return;

    if (start.Name != "start" && char.IsLower(start.Name[0]))
        smallsVisited.Add(start);

    var traverseStart = GetVertexByName("start");
    var neighbours = _adjacencyList[start].Where(v => v != traverseStart);
    foreach (var end in neighbours)
    {
        _path.Add(new Edge(start, end));
        Traverse(end);
    }
}

我用名為start的頂點調用Traverse來設置過程滾動。 當參數start是名為end的頂點時,我希望遍歷停止,但結果只是一個路徑,即 vertex start到 vertex b

我對使用圖表非常陌生,只有幾天時間,並且對遞歸非常生疏。 我究竟做錯了什么?

以下是我如何處理遞歸遍歷它。 我已將圖形設置為鄰居列表的頂點名稱字典。 此外,由於“開始”以小寫字符開頭,我只是依靠它而不是 go 通過該頂點返回。 這里的另一個技巧是將路徑傳遞到當前頂點,以便您可以在每個遞歸中構建它,但每個遞歸都不會受到其他遞歸的影響。 既然你有路徑,你可以檢查它是否包含小寫的頂點。 另請注意,我們仍然可以根據圖表獲得無限路徑和 StackOverflow。 例如,如果您的圖表將“b”更改為“B”,那么您可以在“A”和“B”之間無限次移動,並且此代碼不會以任何方式檢測到這一點。

void Main()
{
    var graph = new Dictionary<string, List<string>>
    {
        ["start"] = new List<string>{"A", "b"},
        ["A"] = new List<string>{"c", "b", "end", "start"},
        ["b"] = new List<string>{"A", "d", "end", "start"},
        ["c"] = new List<string>{"A"},
        ["d"] = new List<string>{"b"},
        ["end"] = new List<string>{"A", "b"},
    };
    
    var result = Traverse(graph, "start", "end");

    foreach(var x in result)
    {
        Console.WriteLine(string.Join("-", x));
    }
}

public static IEnumerable<IEnumerable<string>> Traverse(
    Dictionary<string, List<string>> graph, 
    string current, 
    string end, 
    IEnumerable<string> path = null)
{
    // Initialize the path if we don't have one yet.
    path ??= Enumerable.Empty<string>();
    
    // Do not allow the path to go to a lower cast vertex 
    // more than once.
    if(char.IsLower(current[0]) && path.Contains(current))
    {
        yield break;
    }
    
    path = path.Append(current);
    
    // If we are at the end then return the path and break.
    if(current == end)
    {
        yield return path;
        yield break;
    }
    
    // Find all the paths from the current vertex through
    // each of it's neighbors and return them.
    foreach(var neighbor in graph[current])
    {
        foreach(var subPath in Traverse(graph, neighbor, end, path))
        {
            yield return subPath;
        }   
    }   
}

結果是

start-A-c-A-b-A-end
start-A-c-A-b-end
start-A-c-A-end
start-A-b-A-c-A-end
start-A-b-A-end
start-A-b-end
start-A-end
start-b-A-c-A-end
start-b-A-end
start-b-end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM