图algorithm查找两个任意顶点之间的所有连接

我试图确定完成下面描述的任务的最佳时间有效的algorithm。

我有一套logging。 对于这组logging,我有连接数据,它表明来自这组的logging对如何相互连接。 这基本上代表一个无向图,logging是顶点,连接数据是边。

集合中的所有logging都具有连接信息(即,不存在孤立logging;集合中的每个logging连接到集合中的一个或多个其他logging)。

我想从集合中select任何两条logging,并能够显示所选logging之间的所有简单path。 “简单path”是指path中没有重复logging的path(即仅有限path)。

注意:两个select的logging总是不同的(即开始和结束的顶点不会是相同的;没有循环)。

例如:

    如果我有以下logging:
         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)表示loggingA连接到loggingB]

如果我selectB作为我的起始logging,E作为我的结束logging,我想通过连接loggingB到loggingE的logging连接find所有简单path。

   连接B到E的所有path:
       B->电子
       B-> F->电子
       B-> F-> C->电子
       B-> A-> C->电子
       B-> A-> C-> F->电子

这是一个例子,在实践中,我可能有成百上千的logging。

看起来,这可以通过深度优先search图来完成。 深度优先search将find两个节点之间的所有非循环path。 这个algorithm应该是非常快的,并且可以扩展到大图(图数据结构是稀疏的,所以它只使用尽可能多的内存)。

我注意到你上面指定的图只有一个方向的边(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(); } } 

节目输出:

 BEBACEBACFEBFEBFCE 

美国国家标准与技术研究院(NIST)的algorithm和数据结构在线词典列出了这个问题为“ 所有简单path”,并build议进行深度优先search 。 CLRS提供相关的algorithm。

在这里可以find使用Petri网的聪明技巧

这是我提出的伪代码。 这不是任何特定的伪代码方言,但应该简单到可以遵循。

任何人都想挑选这个。

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

  • [x]是满足条件的path列表

  • [s]是源顶点

  • [d]是目标顶点

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

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

      1 PathList [p]
      2 ListOfPathLists [x]
      3顶点[s],[d]

      4 PathFind(顶点[c])
      5将[c]添加到列表的尾部[p]
      6对于与[c]相邻的每个顶点[v]
      7如果[v]等于[d],那么
      8保存列表[p]在[x]
      9否则如果[v]不在列表[p]
     10 PathFind([v])
     11下一个为
     12从[p]删除尾巴
     13返回

由于在这个答案中给出的现有的非recursionDFS实现似乎被打破,让我提供一个实际工作。

我用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 

此代码维护两个并行堆栈:一个包含当前path中的较早节点,另一个包含节点堆栈中每个节点的当前邻居索引(这样当我们将其从一个节点的邻居中popup时,堆栈)。 我可以同样使用一堆(节点,索引)对,但我认为双栈方法更具可读性,对于其他语言的用户也许更容易实现。

这段代码还使用一个单独的visited集合,它总是包含当前节点和堆栈中的任何节点,以便高效地检查节点是否已经是当前path的一部分。 如果你的语言碰巧有一个“有序集合”的数据结构,既能提供高效的栈式推/弹操作又能提供高效的成员查询,你可以将它用于节点栈,摆脱单独的visited集合。

或者,如果您为节点使用定制的可变类/结构,则可以在每个节点中存储一个布尔标志,以指示它是否作为当前searchpath的一部分被访问。 当然,这种方法不会让你在同一个图上同时运行两个search,如果你出于某种原因希望这样做。

以下是一些testing代码,演示了上述function如何工作:

 # 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

请注意,虽然此示例图是无向的(即其所有边都是双向的),但该algorithm也适用于任意有向图。 例如,删除C -> B边(通过从B的邻居列表中删除B )产生除第三条path( A -> C -> B -> D )以外的相同输出,这是不可能的。


PS。 构build简单的searchalgorithm(如本主题中给出的其他searchalgorithm)的性能很差。

例如,考虑在无向图上find从A到B的所有path的任务,其中起始节点A具有两个邻居:目标节点B(其不具有除A之外的其他邻居)和属于团的一部分的节点C n + 1节点,如下所示:

 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之间的唯一path是直接path,但是从节点A开始的一个天真的DFS将浪费O( n !)时间在集团内部无用地探索path,即使对于人这些path都不可能导致B.

也可以构build具有相似属性的DAG ,例如通过使起始节点A连接目标节点B和连接到节点D 1和D 2的两个其他节点C 1和C 2 ,这两个节点都连接到E 1和E 2等等。 对于像这样排列的n层节点,对于从A到B的所有path的幼稚search将最终浪费O(2 n )时间,在放弃之前检查所有可能的死angular。

当然,从集团中的一个节点(除了C)或从DAG的最后一个层向目标节点B添加边缘创build从A到B的指数级大量的可能path,并且a纯粹的本地searchalgorithm不能预先知道是否会发现这样的边缘。 因此,从某种意义上说,这种天真search的输出敏感度较差是由于他们对图的全局结构缺乏了解。

虽然可以使用各种预处理方法(如迭代消除叶节点,search单节点顶点分隔符等)来避免这些“指数时间死angular”,但我不知道任何一般的预处理技巧,可以在任何情况下消除它们。 一个通用的解决scheme是在search的每一步中检查目标节点是否仍然可以使用(使用子search),如果不是,则提前回溯 -​​ 但是,这样会显着减慢search速度,与图的大小成比例),对于许多包含这种病理性死端的图。

与二楼相比,这是一个逻辑上更好看的recursion版本。

 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); } } } } 

节目输出

 BACEBACFEBE BFCE BFE 

C代码解决scheme。 它基于使用最小内存的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); } 

最近我已经解决了类似的问题,而不是我只对最短的所有解决scheme。

我使用了“宽度第一”的迭代search,它使用了一个状态队列,每个状态都包含一个包含图上当前点的logging以及到达那里的path。

您从队列中的单个logging开始,它具有起始节点和空path。

通过代码的每次迭代都将项目从列表头移开,并检查它是否是一个解决scheme(节点到达的是你想要的,如果是的话,我们完成了),否则,它构造一个新的将节点连接到当前节点的队列项目,并修改基于前一节点path的path,并在末尾附加新的跳转。

现在,您可以使用类似的东西,但是当您find解决scheme而不是停止时,将该解决scheme添加到“find的列表”中并继续。

你需要跟踪一个访问过的节点列表,这样你就不会自行回溯,否则你会有一个无限循环。

如果你想更多的伪代码发表评论什么的,我会详细说明。

我想你应该描述你背后的真正的问题。 我这样说是因为你要求一些有效的时间,但是这个问题的答案似乎成指数增长!

所以我不会指望比指数更好的algorithm。

我会做回溯,并通过整个图表。 为了避免周期,一路上保存所有访问节点。 当您返回时,取消标记该节点。

使用recursion:

 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 } 

或者那是错的?

编辑:哦,我忘了:你应该消除利用该节点堆栈的recursion调用

基本原理是你不必担心graphics。这是标准问题,称为dynamic连接问题。 有以下types的方法可以实现节点的连接与否:

  1. 快速查找
  2. 快速联盟
  3. 改进的algorithm(两者的组合)

这里是C代码,我已经尝试了最小的时间复杂度O(log * n)这意味着对于65536列表的边缘,它需要4search和2 ^ 65536,它需要5search。 我从普林斯顿大学algorithm课程分享我的实施

提示:您可以从上面共享的链接中find适当解释的Java解决scheme。

 /* 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; } 

这可能比较晚,但是这里是来自Casey的Java中DFSalgorithm的C#版本,它使用堆栈遍历两个节点之间的所有path。 一如往常,recursion的可读性更好。

  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(); } } 
这是一个示例图来testing:

     //示例图。 数字是边缘ID
     // 1 3       
     // A --- B --- C ----
     // |  |  2 |
     // |  4 ----- D |
     // ------------------

这是我头顶的一个想法:

  1. find一个连接。 (深度优先search可能是一个很好的algorithm,因为path长度并不重要。)
  2. 禁用最后一个段。
  3. 在先前禁用的连接之前尝试从最后一个节点查找另一个连接。
  4. 转到2,直到没有更多的连接。

据我所知,Ryan Fox( 58343) ,Christian( 58444 )和你自己( 58461 )给出的解决scheme大致如此 ,我不相信在这种情况下,宽度优先遍历是有帮助的。 (A,B)(A,C)(B,C)(B,D)(C,D)你将得到pathABDACD ,而不是ABCD

我find了枚举所有path的方法,包括包含循环的无限path。

http://blog.vjeux.com/2009/project/project-shortest-path.html

寻找primefacespath和循环

 Definition 

我们要做的是find从A点到B点的所有可能的path。由于涉及到循环,所以不能只是遍历并枚举它们。 相反,你将不得不寻找primefacespath不循环和尽可能小的周期(你不希望你的周期重复)。

我对primefacespath的第一个定义是不通过同一个节点两次的path。 但是,我发现那并不是所有的可能性。 经过一番反思后,我发现节点并不重要,但边缘是! 所以primefacespath是两次不通过同一个边的path。

这个定义是方便的,它也适用于循环:A点的primefaces循环是从A点到A点的primefacespath。

履行

 Atomic Paths A -> B 

为了获得从A点开始的所有path,我们将从A点recursion地遍历图。在通过一个孩子的时候,我们要做一个链接child – > parent,以便知道所有的边。已经越过了。 在我们去那个孩子之前,我们必须遍历那个链表,并且确定指定的边缘还没有被走过。

当我们到达目的地时,我们可以存储我们find的path。

 Freeing the list 

当你想释放链表时会发生问题。 它基本上是以相反顺序链接的一棵树。 一个解决scheme是双链接该列表,当所有的primefacespath被发现,释放树的起点。

但一个聪明的解决scheme是使用引用计数(从垃圾收集启发)。 每次你给父母添加一个链接,你的引用计数就加一个。 然后,当你到达一个path的末端时,你可以向后退出,而引用计数等于1.如果它更高,你只需删除一个,然后停下来。

 Atomic Cycle A 

寻找A的primefaces周期与寻找A到A的primefacespath是一样的。但是我们可以做几个优化。 首先,当我们到达目的地点时,我们只想在边缘成本总和为负的情况下保存path:我们只想通过吸收周期。

正如你以前所看到的,当查找primefacespath时,整个graphics将被遍历。 相反,我们可以将search区域限制为包含A的强连通组件。查找这些组件需要使用Tarjanalgorithm对图进行简单的遍历。

结合primefacespath和循环

此时,我们拥有从A到B以及每个节点的所有primefaces周期的所有primefacespath,留给我们来组织一切以获得最短path。 从现在开始,我们将研究如何在primefacespath中findprimefaces周期的最佳组合。

正如其他一些海报所描述的那样,问题简而言之就是使用深度优先searchalgorithm来recursionsearchgraphics以获得通信端节点之间的所有path组合。

algorithm本身开始于你给出的开始节点,检查其所有的输出链接,并通过展开出现的search树的第一个子节点,逐渐深入search直到find目标节点,或直到它遇到节点没有孩子

然后search回溯,返回到最近的节点还没有完成探索。

我最近在这个话题上做了博客 ,在这个过程中发布了一个C ++实现的例子。

加上Casey Watson的回答,这里是另一个Java实现。 用启动节点初始化访问节点。

 private void getPaths(Graph graph, LinkedList<String> visitedNodes) { LinkedList<String> adjacent = graph.getAdjacent(visitedNodes.getLast()); for(String node : adjacent){ if(visitedNodes.contains(node)){ continue; } if(node.equals(END)){ visitedNodes.add(node); printPath(visitedNodes); visitedNodes.removeLast(); } visitedNodes.add(node); getPaths(graph, visitedNodes); visitedNodes.removeLast(); } } 

find_paths [s,t,d,k]

这个问题已经老了,已经回答了。 然而,没有一个显示可能是一个更灵活的algorithm来完成相同的事情。 所以我会把帽子扔进戒指

我个人发现find_paths[s, t, d, k]forms的algorithm很有用,其中:

  • s是起始节点
  • t是目标节点
  • d是要search的最大深度
  • k是要find的path的数量

使用你的编程语言的dk的无穷大forms将给你所有的path。

很明显,如果你使用的是有向图,并且你想要在st之间s所有无向path,你将不得不以这两种方式运行:

 find_paths[s, t, d, k] <join> find_paths[t, s, d, k] 

帮助函数

我个人喜欢recursion,虽然有时候会困难,反正先定义我们的帮助函数:

 def find_paths_recursion(graph, current, goal, current_depth, max_depth, num_paths, current_path, paths_found) current_path.append(current) if current_depth > max_depth: return if current == goal: if len(paths_found) <= number_of_paths_to_find: paths_found.append(copy(current_path)) current_path.pop() return else: for successor in graph[current]: self.find_paths_recursion(graph, successor, goal, current_depth + 1, max_depth, num_paths, current_path, paths_found) current_path.pop() 

主function

有了这个,核心function是微不足道的:

 def find_paths[s, t, d, k]: paths_found = [] # PASSING THIS BY REFERENCE find_paths_recursion(s, t, 0, d, k, [], paths_found) 

首先,让我们注意一些事情:

  • 上面的伪代码是语言的混搭,但最像Python(因为我只是编码)。 严格的复制粘贴将不起作用。
  • []是一个未初始化的列表,将其replace为您select的编程语言的等价物
  • paths_found通过引用传递。 很显然recursion函数不返回任何东西。 正确处理。
  • 这里的graph是假设某种forms的hashed结构。 有很多方法来实现一个graphics。 Either way, graph[vertex] gets you a list of adjacent vertices in a directed graph – adjust accordingly.
  • this assumes you have pre-processed to remove "buckles" (self-loops), cycles and multi-edges