图算法:查找任意两个顶点之间的所有连接。

121

我正在尝试确定最有效的算法来完成以下描述的任务。

我有一组记录。对于这组记录,我有连接数据,它指示此集合中的记录成对连接在一起的方式。这基本上表示一个无向图,其中记录是顶点,连接数据是边缘。

集合中的所有记录都有连接信息(即不存在孤立的记录;集合中的每个记录都连接到另一个或多个记录)。

我想选择集合中的任意两个记录,并能够显示所选记录之间的所有简单路径。所谓“简单路径”是指路径中不重复出现记录的路径(即仅有有限路径)。

注意:所选的两个记录将始终不同(即起始和结束顶点永远不会相同;没有循环)。

例如:

    如果我有以下记录:
        A, B, C, D, E
并且以下表示连接: (A,B),(A,C),(B,A),(B,D),(B,E),(B,F),(C,A),(C,E), (C,F),(D,B),(E,C),(E,F),(F,B),(F,C),(F,E)
[其中(A,B)表示记录A连接到记录B]

如果我选择B作为开始记录,选择E作为结束记录,我希望找到通过记录连接连接记录B到记录E的所有简单路径。

   连接B到E的所有路径:
      B->E
      B->F->E
      B->F->C->E
      B->A->C->E
      B->A->C->F->E

这是一个例子,在实践中,我可能有包含数十万条记录的集合。


连接被称为“循环”,并且此答案为您提供了大量信息。 - elhoim
3
请说明您需要有限的、没有回路的连接列表,还是包含所有可能回路的无限连接流。参见Blorgbeard的答案。 - Charles Stewart
有人能帮忙吗???http://stackoverflow.com/questions/32516706/how-to-create-path-if-i-have-total-number-of-nodes-and-distance-between-them?noredirect=1#comment52892493_32516706 - tejas3006
17个回答

122
似乎可以通过对图进行深度优先搜索来实现。深度优先搜索将找到两个节点之间的所有非循环路径。这种算法应该非常快,并且适用于大型图(图数据结构是稀疏的,因此只使用所需的内存)。
我注意到您指定的图仅具有一个有向边(B、E)。这是一个笔误还是真的是一个有向图?无论如何,这个解决方案都有效。很抱歉我不能用C语言实现,我在那方面有点薄弱。不过,我相信您应该能够轻松地翻译出这段Java代码。
Graph.java:
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

public class Graph {
    private Map<String, LinkedHashSet<String>> map = new HashMap();

    public void addEdge(String node1, String node2) {
        LinkedHashSet<String> adjacent = map.get(node1);
        if(adjacent==null) {
            adjacent = new LinkedHashSet();
            map.put(node1, adjacent);
        }
        adjacent.add(node2);
    }

    public void addTwoWayVertex(String node1, String node2) {
        addEdge(node1, node2);
        addEdge(node2, node1);
    }

    public boolean isConnected(String node1, String node2) {
        Set adjacent = map.get(node1);
        if(adjacent==null) {
            return false;
        }
        return adjacent.contains(node2);
    }

    public LinkedList<String> adjacentNodes(String last) {
        LinkedHashSet<String> adjacent = map.get(last);
        if(adjacent==null) {
            return new LinkedList();
        }
        return new LinkedList<String>(adjacent);
    }
}

Search.java:

import java.util.LinkedList;

public class Search {

    private static final String START = "B";
    private static final String END = "E";

    public static void main(String[] args) {
        // this graph is directional
        Graph graph = new Graph();
        graph.addEdge("A", "B");
        graph.addEdge("A", "C");
        graph.addEdge("B", "A");
        graph.addEdge("B", "D");
        graph.addEdge("B", "E"); // this is the only one-way connection
        graph.addEdge("B", "F");
        graph.addEdge("C", "A");
        graph.addEdge("C", "E");
        graph.addEdge("C", "F");
        graph.addEdge("D", "B");
        graph.addEdge("E", "C");
        graph.addEdge("E", "F");
        graph.addEdge("F", "B");
        graph.addEdge("F", "C");
        graph.addEdge("F", "E");
        LinkedList<String> visited = new LinkedList();
        visited.add(START);
        new Search().depthFirst(graph, visited);
    }

    private void depthFirst(Graph graph, LinkedList<String> visited) {
        LinkedList<String> nodes = graph.adjacentNodes(visited.getLast());
        // examine adjacent nodes
        for (String node : nodes) {
            if (visited.contains(node)) {
                continue;
            }
            if (node.equals(END)) {
                visited.add(node);
                printPath(visited);
                visited.removeLast();
                break;
            }
        }
        for (String node : nodes) {
            if (visited.contains(node) || node.equals(END)) {
                continue;
            }
            visited.addLast(node);
            depthFirst(graph, visited);
            visited.removeLast();
        }
    }

    private void printPath(LinkedList<String> visited) {
        for (String node : visited) {
            System.out.print(node);
            System.out.print(" ");
        }
        System.out.println();
    }
}

程序输出:

B E 
B A C E 
B A C F E 
B F E 
B F C E 

5
请注意,这不是广度优先遍历。广度优先遍历先访问距离根节点为0的所有节点,然后是距离为1的节点,然后是距离为2的节点,以此类推。 - mweerden
15
没错,这是深度优先搜索。广度优先搜索需要使用队列,在所有的第N层节点被处理完之后,将第N+1层的节点入队等待处理。然而,对于原帖的目的来说,无论是广度优先搜索还是深度优先搜索都可以胜任,因为没有指定路径的首选排序顺序。 - Matt J
1
Casey,我已经寻找解决这个问题的方法很久了。最近我在C++中实现了这个DFS算法,效果非常好。 - AndyUK
6
递归的缺点是,如果你有一个深度很大的图形(A->B->C->...->N),在Java中可能会出现StackOverflowError。 - Rrr
1
我在下面添加了一个C#的迭代版本。 - batta
显示剩余8条评论

24

美国国家标准与技术研究院(NIST)的算法和数据结构在线词典将这个问题列为"所有简单路径", 并推荐使用深度优先搜索解决。CLRS提供了相关算法。

一种巧妙利用Petri网的技术在这里发现。


2
你能帮我想一个更好的解决方案吗?一个深度优先搜索要花费非常长的时间来运行:https://dev59.com/bGsy5IYBdhLWcg3wvgr7 - Pacerier
请注意,即使两个节点之间的所有简单路径集合很小且易于找到,也很容易构造出DFS效率非常低下的图形。例如,考虑一个无向图,其中起始节点A有两个邻居:目标节点B(除了A之外没有其他邻居)和一个节点C,它是一个完全连接的大小为n + 1的团。尽管从A到B显然只有一条简单路径,但是朴素的DFS将浪费O(n!)的时间来无用地探索该团。类似的例子(一个解决方案,DFS需要指数时间)也可以在DAG中找到。 - Ilmari Karonen
NIST表示:“可以使用深度优先搜索枚举路径。” - chomp

13

以下是我想到的伪代码。这不是特定的伪代码方言,但应该足够简单易懂。

有人想拆开吗?

  • [p] 是表示当前路径的顶点列表。

  • [x] 是符合条件的路径列表。

  • [s] 是源顶点。

  • [d] 是目标顶点。

  • [c] 是当前顶点(PathFind例程的参数)。

假设有一种有效的方法来查找相邻的顶点(第6行)。

     1 路径列表 [p]
     2 路径列表的列表 [x]
     3 顶点[s],[d]
4 PathFind ( Vertex [c] ) 5 将[c]添加到列表[p]的末尾 6 对于每个与[c]相邻的顶点[v] 7 如果[v]等于[d],则 8 将列表[p]保存在[x]中 9 否则,如果[v]不在列表[p]中 10 PathFind([v]) 11 下一个循环 12 从[p]中删除末尾 13 返回

请问您能否解释一下第11步和第12步的内容? - bozo user
第11行只是表示与从第6行开始的For循环相对应的结束块。第12行意味着在返回给调用者之前删除路径列表的最后一个元素。 - Robert Groves
PathFind的初始调用是什么 - 你是否传递源顶点[s]? - bozo user
在这个例子中可以,但请记住,您可能不希望编写与此伪代码一一对应的真实代码。它更多是为了说明一种思维过程,而不是设计良好的代码。 - Robert Groves

8

由于这个答案中给出的现有非递归DFS实现似乎有问题,所以让我提供一个实际可行的实现。

我用Python编写了这个实现,因为我认为它非常易读且没有实现细节的混杂(并且因为它具有实现生成器的便捷关键字yield),但是将其移植到其他语言应该相当容易。

# a generator function to find all simple paths between two nodes in a
# graph, represented as a dictionary that maps nodes to their neighbors
def find_simple_paths(graph, start, end):
    visited = set()
    visited.add(start)

    nodestack = list()
    indexstack = list()
    current = start
    i = 0

    while True:
        # get a list of the neighbors of the current node
        neighbors = graph[current]

        # find the next unvisited neighbor of this node, if any
        while i < len(neighbors) and neighbors[i] in visited: i += 1

        if i >= len(neighbors):
            # we've reached the last neighbor of this node, backtrack
            visited.remove(current)
            if len(nodestack) < 1: break  # can't backtrack, stop!
            current = nodestack.pop()
            i = indexstack.pop()
        elif neighbors[i] == end:
            # yay, we found the target node! let the caller process the path
            yield nodestack + [current, end]
            i += 1
        else:
            # push current node and index onto stacks, switch to neighbor
            nodestack.append(current)
            indexstack.append(i+1)
            visited.add(neighbors[i])
            current = neighbors[i]
            i = 0

这段代码维护着两个平行的栈:一个包含当前路径中较早的节点,另一个包含每个节点在节点栈中的当前邻居索引(这样我们就可以在将其从栈中弹出时恢复遍历节点的邻居)。我本可以使用单个(node, index)对的栈,但我认为两个栈的方法更易读,也许对其他语言的用户更容易实现。
此代码还使用了一个单独的“visited”集合,它始终包含当前节点和栈上的任何节点,以便让我高效地检查节点是否已经是当前路径的一部分。如果你的语言恰好有一个“有序集合”数据结构,它提供了高效的类似栈的推/弹操作和高效的成员资格查询,你可以将其用于节点栈并摆脱单独的“visited”集合。
或者,如果您正在使用自定义可变类/结构来表示节点,则可以在每个节点中存储一个布尔标志,以指示它是否已作为当前搜索路径的一部分被访问。当然,这种方法不能让您在同一图上并行运行两个搜索,如果您由于某种原因希望这样做。
以下是一些测试代码,演示了上面给出的函数如何工作:
# test graph:
#     ,---B---.
#     A   |   D
#     `---C---'
graph = {
    "A": ("B", "C"),
    "B": ("A", "C", "D"),
    "C": ("A", "B", "D"),
    "D": ("B", "C"),
}

# find paths from A to D
for path in find_simple_paths(graph, "A", "D"): print " -> ".join(path)

在给定的示例图上运行此代码会产生以下输出:
A -> B -> C -> D
A -> B -> D
A -> C -> B -> D
A -> C -> D
请注意,虽然此示例图是无向的(即所有边都双向),但该算法也适用于任意有向图。例如,通过删除 C -> B 边缘(通过从 C 的邻居列表中删除 B)可以获得相同的输出,除了第三条路径(A -> C -> B -> D),这不再可能。

附注:构建一些图形很容易使得像这个(以及本帖中给出的其他算法)这样的简单搜索算法表现非常差。

例如,考虑在无向图上查找从A到B的所有路径的任务,其中起始节点A有两个邻居:目标节点B(除了A之外没有其他邻居)和一个由n+1个节点组成的团体的节点C,如下所示:

graph = {
    "A": ("B", "C"),
    "B": ("A"),
    "C": ("A", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "D": ("C", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "E": ("C", "D", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "F": ("C", "D", "E", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "G": ("C", "D", "E", "F", "H", "I", "J", "K", "L", "M", "N", "O"),
    "H": ("C", "D", "E", "F", "G", "I", "J", "K", "L", "M", "N", "O"),
    "I": ("C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "O"),
    "J": ("C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "O"),
    "K": ("C", "D", "E", "F", "G", "H", "I", "J", "L", "M", "N", "O"),
    "L": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "M", "N", "O"),
    "M": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "N", "O"),
    "N": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "O"),
    "O": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N"),
}

很容易看出A和B之间的唯一路径是直接的,但从节点A开始的天真DFS将浪费O(n!)时间在团中探索无用的路径,即使对于人类来说,这些路径显然都不可能通向B。
人们还可以构造具有类似特性的DAGs,例如通过使起始节点A连接目标节点B和另外两个节点C1和C2,这两个节点都连接到节点D1和D2,这两个节点又连接到E1和E2,依此类推。对于像这样排列的n层节点,搜索所有从A到B的路径的天真搜索最终将浪费O(2n)时间,在放弃之前检查所有可能的死路。
当然,从团中的一个节点(C 以外的节点)或者 DAG 的最后一层向目标节点 B 添加边将会导致从 A 到 B 可能出现指数级数量的路径,一个纯粹的局部搜索算法事实上无法预先知道是否会找到这样的边。因此,在某种程度上,这种天真搜索的不良 输出敏感性 是由于它们缺乏对图形全局结构的认识。
虽然有各种预处理方法(例如迭代地消除叶节点、寻找单节点顶点分隔器等)可以用来避免一些这样的“指数时间死胡同”,但我不知道有任何通用的预处理技巧可以在所有情况下消除它们。一种通用解决方案是在搜索的每一步检查目标节点是否仍然可达(使用子搜索),如果不能,则尽早回溯,但遗憾的是,对于许多不包含这种病态死胡同的图形来说,这将显著减慢搜索速度(在最坏情况下,与图形大小成比例)。

1
那就是我正在寻找的,谢谢 :) - arslan
感谢您提供非递归DFS解决方案。请注意,打印结果的最后一行存在语法错误,应该是 for path in find_simple_paths(graph, "A", "D"): print(" -> ".join(path)),缺少括号。 - David Oliván
1
@DavidOlivánUbieto:这是Python 2代码,所以没有括号。 :) - Ilmari Karonen

5
这里有一个逻辑上更好看的递归版本,与第二个楼层相比。
public class Search {

private static final String START = "B";
private static final String END = "E";

public static void main(String[] args) {
    // this graph is directional
    Graph graph = new Graph();
    graph.addEdge("A", "B");
    graph.addEdge("A", "C");
    graph.addEdge("B", "A");
    graph.addEdge("B", "D");
    graph.addEdge("B", "E"); // this is the only one-way connection
    graph.addEdge("B", "F");
    graph.addEdge("C", "A");
    graph.addEdge("C", "E");
    graph.addEdge("C", "F");
    graph.addEdge("D", "B");
    graph.addEdge("E", "C");
    graph.addEdge("E", "F");
    graph.addEdge("F", "B");
    graph.addEdge("F", "C");
    graph.addEdge("F", "E");
    List<ArrayList<String>> paths = new ArrayList<ArrayList<String>>();
    String currentNode = START;
    List<String> visited = new ArrayList<String>();
    visited.add(START);
    new Search().findAllPaths(graph, seen, paths, currentNode);
    for(ArrayList<String> path : paths){
        for (String node : path) {
            System.out.print(node);
            System.out.print(" ");
        }
        System.out.println();
    }   
}

private void findAllPaths(Graph graph, List<String> visited, List<ArrayList<String>> paths, String currentNode) {        
    if (currentNode.equals(END)) { 
        paths.add(new ArrayList(Arrays.asList(visited.toArray())));
        return;
    }
    else {
        LinkedList<String> nodes = graph.adjacentNodes(currentNode);    
        for (String node : nodes) {
            if (visited.contains(node)) {
                continue;
            } 
            List<String> temp = new ArrayList<String>();
            temp.addAll(visited);
            temp.add(node);          
            findAllPaths(graph, temp, paths, node);
        }
    }
}
}

程序输出
B A C E 

B A C F E 

B E

B F C E

B F E 

4

C代码解决方案。它基于使用最少内存的DFS算法。

#include <stdio.h>
#include <stdbool.h>

#define maxN    20  

struct  nodeLink
{

    char node1;
    char node2;

};

struct  stack
{   
    int sp;
    char    node[maxN];
};   

void    initStk(stk)
struct  stack   *stk;
{
    int i;
    for (i = 0; i < maxN; i++)
        stk->node[i] = ' ';
    stk->sp = -1;   
}

void    pushIn(stk, node)
struct  stack   *stk;
char    node;
{

    stk->sp++;
    stk->node[stk->sp] = node;

}    

void    popOutAll(stk)
struct  stack   *stk;
{

    char    node;
    int i, stkN = stk->sp;

    for (i = 0; i <= stkN; i++)
    {
        node = stk->node[i];
        if (i == 0)
            printf("src node : %c", node);
        else if (i == stkN)
            printf(" => %c : dst node.\n", node);
        else
            printf(" => %c ", node);
    }

}


/* Test whether the node already exists in the stack    */
bool    InStack(stk, InterN)
struct  stack   *stk;
char    InterN;
{

    int i, stkN = stk->sp;  /* 0-based  */
    bool    rtn = false;    

    for (i = 0; i <= stkN; i++)
    {
        if (stk->node[i] == InterN)
        {
            rtn = true;
            break;
        }
    }

    return     rtn;

}

char    otherNode(targetNode, lnkNode)
char    targetNode;
struct  nodeLink    *lnkNode;
{

    return  (lnkNode->node1 == targetNode) ? lnkNode->node2 : lnkNode->node1;

}

int entries = 8;
struct  nodeLink    topo[maxN]    =       
    {
        {'b', 'a'}, 
        {'b', 'e'}, 
        {'b', 'd'}, 
        {'f', 'b'}, 
        {'a', 'c'},
        {'c', 'f'}, 
        {'c', 'e'},
        {'f', 'e'},               
    };

char    srcNode = 'b', dstN = 'e';      

int reachTime;  

void    InterNode(interN, stk)
char    interN;
struct  stack   *stk;
{

    char    otherInterN;
    int i, numInterN = 0;
    static  int entryTime   =   0;

    entryTime++;

    for (i = 0; i < entries; i++)
    {

        if (topo[i].node1 != interN  && topo[i].node2 != interN) 
        {
            continue;   
        }

        otherInterN = otherNode(interN, &topo[i]);

        numInterN++;

        if (otherInterN == stk->node[stk->sp - 1])
        {
            continue;   
        }

        /*  Loop avoidance: abandon the route   */
        if (InStack(stk, otherInterN) == true)
        {
            continue;   
        }

        pushIn(stk, otherInterN);

        if (otherInterN == dstN)
        {
            popOutAll(stk);
            reachTime++;
            stk->sp --;   /*    back trace one node  */
            continue;
        }
        else
            InterNode(otherInterN, stk);

    }

        stk->sp --;

}


int    main()

{

    struct  stack   stk;

    initStk(&stk);
    pushIn(&stk, srcNode);  

    reachTime = 0;
    InterNode(srcNode, &stk);

    printf("\nNumber of all possible and unique routes = %d\n", reachTime);

}

3

可能有些晚了,但这里是Casey使用C#编写的深度优先搜索算法的版本,可以使用堆栈遍历两个节点之间的所有路径。递归总是更易于阅读。

    void DepthFirstIterative(T start, T endNode)
    {
        var visited = new LinkedList<T>();
        var stack = new Stack<T>();

        stack.Push(start);

        while (stack.Count != 0)
        {
            var current = stack.Pop();

            if (visited.Contains(current))
                continue;

            visited.AddLast(current);

            var neighbours = AdjacentNodes(current);

            foreach (var neighbour in neighbours)
            {
                if (visited.Contains(neighbour))
                    continue;

                if (neighbour.Equals(endNode))
                {
                    visited.AddLast(neighbour);
                    printPath(visited));
                    visited.RemoveLast();
                    break;
                }
            }

            bool isPushed = false;
            foreach (var neighbour in neighbours.Reverse())
            {
                if (neighbour.Equals(endNode) || visited.Contains(neighbour) || stack.Contains(neighbour))
                {
                    continue;
                }

                isPushed = true;
                stack.Push(neighbour);
            }

            if (!isPushed)
                visited.RemoveLast();
        }
    }

这是一个用于测试的示例图:
// 示例图。数字代表边缘id // 1 3 // A --- B --- C ---- // | | 2 | // | 4 ----- D | // ------------------

1
关于您如何用基于堆栈的迭代替换递归的优秀做法。 - Siddhartha Ghosh
我还是不明白,neighbours.Reverse() 是什么?它是 List<T>.Reverse 吗? - user1108948
我检查了这个非递归版本,但似乎不正确。递归版本是好的。也许在改成非递归时出现了一个小错误。 - arslan
@alim: 同意,这段代码有问题。(回溯时未正确移除访问集中的节点,而且栈处理也似乎出了问题。我尝试过看是否可以修复它,但这基本上需要完全重写。) 我刚刚添加了一个答案,提供了一个正确、可工作的非递归解决方案(用 Python 编写,但应该相对容易移植到其他语言)。 - Ilmari Karonen
@Love:是的,neighbors就是邻接表。 - batta
显示剩余3条评论

1

我认为你应该描述一下这背后的真正问题。我这么说是因为你要求时间效率,但问题的答案集似乎呈指数级增长!

因此,我不会期望比指数级更好的算法。

我会使用回溯和遍历整个图形。为了避免循环,沿途保存所有访问过的节点。当你回去时,取消标记节点。

使用递归:

static bool[] visited;//all false
Stack<int> currentway; initialize empty

function findnodes(int nextnode)
{
if (nextnode==destnode)
{
  print currentway 
  return;
}
visited[nextnode]=true;
Push nextnode to the end of currentway.
for each node n accesible from nextnode:
  findnodes(n);
visited[nextnode]=false; 
pop from currenteay
}

这是错的吗?

编辑: 哦,我忘了: 您应该通过利用该节点堆栈来消除递归调用。


我的真正问题与我所描述的完全相同,只是数据集更大而已。我同意这似乎随着数据集的大小呈指数增长。 - Robert Groves

1
基本原则是你不需要担心图表。这是一个称为动态连通性问题的标准问题。以下是可以实现节点是否连接的方法类型:
  1. Quick Find
  2. Quick Union
  3. 改进算法(两者的结合)
这里是我尝试使用最小时间复杂度O(log*n)的C代码,这意味着对于65536条边的列表,它需要4次搜索,而对于2^65536,它需要5次搜索。我从算法中分享了我的实现:普林斯顿大学算法课程 提示:您可以在上面分享的链接中找到Java解决方案并得到适当的解释。
/* Checking Connection Between Two Edges */

#include<stdio.h>
#include<stdlib.h>
#define MAX 100

/*
  Data structure used

vertex[] - used to Store The vertices
size - No. of vertices
sz[] - size of child's
*/

/*Function Declaration */
void initalize(int *vertex, int *sz, int size);
int root(int *vertex, int i);
void add(int *vertex, int *sz, int p, int q);
int connected(int *vertex, int p, int q);

int main() //Main Function
{ 
char filename[50], ch, ch1[MAX];
int temp = 0, *vertex, first = 0, node1, node2, size = 0, *sz;
FILE *fp;


printf("Enter the filename - "); //Accept File Name
scanf("%s", filename);
fp = fopen(filename, "r");
if (fp == NULL)
{
    printf("File does not exist");
    exit(1);
}
while (1)
{
    if (first == 0) //getting no. of vertices
    {
        ch = getc(fp);
        if (temp == 0)
        {
            fseek(fp, -1, 1);
            fscanf(fp, "%s", &ch1);
            fseek(fp, 1, 1);
            temp = 1;
        }
        if (isdigit(ch))
        {
            size = atoi(ch1);
            vertex = (int*) malloc(size * sizeof(int));     //dynamically allocate size  
            sz = (int*) malloc(size * sizeof(int));
            initalize(vertex, sz, size);        //initialization of vertex[] and sz[]
        }
        if (ch == '\n')
        {
            first = 1;
            temp = 0;
        }
    }
    else
    {
        ch = fgetc(fp);
        if (isdigit(ch))
            temp = temp * 10 + (ch - 48);   //calculating value from ch
        else
        {
            /* Validating the file  */

            if (ch != ',' && ch != '\n' && ch != EOF)
            {
                printf("\n\nUnkwown Character Detected.. Exiting..!");

                exit(1);
            }
            if (ch == ',')
                node1 = temp;
            else
            {
                node2 = temp;
                printf("\n\n%d\t%d", node1, node2);
                if (node1 > node2)
                {
                    temp = node1;
                    node1 = node2;
                    node2 = temp;
                }

                /* Adding the input nodes */

                if (!connected(vertex, node1, node2))
                    add(vertex, sz, node1, node2);
            }
            temp = 0;
        }

        if (ch == EOF)
        {
            fclose(fp);
            break;
        }
    }
}

do
{
    printf("\n\n==== check if connected ===");
    printf("\nEnter First Vertex:");
    scanf("%d", &node1);
    printf("\nEnter Second Vertex:");
    scanf("%d", &node2);

    /* Validating The Input */

    if( node1 > size || node2 > size )
    {
        printf("\n\n Invalid Node Value..");
        break;
    }

    /* Checking the connectivity of nodes */

    if (connected(vertex, node1, node2))
        printf("Vertex %d and %d are Connected..!", node1, node2);
    else
        printf("Vertex %d and %d are Not Connected..!", node1, node2);


    printf("\n 0/1:  ");

    scanf("%d", &temp);

} while (temp != 0);

free((void*) vertex);
free((void*) sz);


return 0;
}

void initalize(int *vertex, int *sz, int size) //Initialization of graph
{
int i;
for (i = 0; i < size; i++)
{
    vertex[i] = i;
    sz[i] = 0;
}
}
int root(int *vertex, int i)    //obtaining the root
{
while (i != vertex[i])
{
    vertex[i] = vertex[vertex[i]];
    i = vertex[i];
}
return i;
}

/* Time Complexity for Add --> logn */
void add(int *vertex, int *sz, int p, int q) //Adding of node
{
int i, j;
i = root(vertex, p);
j = root(vertex, q);

/* Adding small subtree in large subtree  */

if (sz[i] < sz[j])
{
    vertex[i] = j;
    sz[j] += sz[i];
}
else
{
    vertex[j] = i;
    sz[i] += sz[j];
}

}

/* Time Complexity for Search -->lg* n */

int connected(int *vertex, int p, int q) //Checking of  connectivity of nodes
{
/* Checking if root is same  */

if (root(vertex, p) == root(vertex, q))
    return 1;

return 0;
}

这似乎并没有解决所提出的问题。OP想要找到两个节点之间的所有简单路径,而不仅仅是检查路径是否存在。 - Ilmari Karonen

1

我最近解决了一个类似的问题,但我只对最短的解决方案感兴趣。

我使用了一种“广度优先”的迭代搜索方法,它使用了一个状态队列,每个状态都包含了当前图上的一个点和到达该点的路径。

你可以从队列中的一个记录开始,该记录包含了起始节点和一个空路径。

代码的每次迭代都会从列表头部取出一个项,并检查它是否是一个解决方案(即到达的节点是你想要的节点)。如果是,我们就完成了。否则,它会构建一个新的队列项,其中包含与当前节点连接的节点,并根据前一个节点的路径,在路径末尾添加新的跳转。

现在,你可以使用类似的方法,但当找到一个解决方案时,不要停止,而是将该解决方案添加到你的“已找到列表”中并继续。

你需要跟踪一个已访问节点列表,以防止重复回溯,否则会陷入无限循环。

如果你想要更多的伪代码,请发表评论或其他意见,我会详细解释。


6
我认为如果你只关心最短路径的话,Dijkstra算法是“解决方案” :)。 - vicatcu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接