找到两个给定节点之间的路径?
Posted
技术标签:
【中文标题】找到两个给定节点之间的路径?【英文标题】:Find the paths between two given nodes? 【发布时间】:2010-10-17 08:14:59 【问题描述】:假设我有按以下方式连接的节点,我如何得出给定点之间存在的路径数量以及路径详细信息?
1,2 //node 1 and 2 are connected
2,3
2,5
4,2
5,11
11,12
6,7
5,6
3,6
6,8
8,10
8,9
找到从 1 到 7 的路径:
回答: 找到 2 条路径,它们是
1,2,3,6,7
1,2,5,6,7
发现here 的实现很好,我将使用相同的
这是上面python链接中的sn-p
# a sample graph
graph = 'A': ['B', 'C','E'],
'B': ['A','C', 'D'],
'C': ['D'],
'D': ['C'],
'E': ['F','D'],
'F': ['C']
class MyQUEUE: # just an implementation of a queue
def __init__(self):
self.holder = []
def enqueue(self,val):
self.holder.append(val)
def dequeue(self):
val = None
try:
val = self.holder[0]
if len(self.holder) == 1:
self.holder = []
else:
self.holder = self.holder[1:]
except:
pass
return val
def IsEmpty(self):
result = False
if len(self.holder) == 0:
result = True
return result
path_queue = MyQUEUE() # now we make a queue
def BFS(graph,start,end,q):
temp_path = [start]
q.enqueue(temp_path)
while q.IsEmpty() == False:
tmp_path = q.dequeue()
last_node = tmp_path[len(tmp_path)-1]
print tmp_path
if last_node == end:
print "VALID_PATH : ",tmp_path
for link_node in graph[last_node]:
if link_node not in tmp_path:
#new_path = []
new_path = tmp_path + [link_node]
q.enqueue(new_path)
BFS(graph,"A","D",path_queue)
-------------results-------------------
['A']
['A', 'B']
['A', 'C']
['A', 'E']
['A', 'B', 'C']
['A', 'B', 'D']
VALID_PATH : ['A', 'B', 'D']
['A', 'C', 'D']
VALID_PATH : ['A', 'C', 'D']
['A', 'E', 'F']
['A', 'E', 'D']
VALID_PATH : ['A', 'E', 'D']
['A', 'B', 'C', 'D']
VALID_PATH : ['A', 'B', 'C', 'D']
['A', 'E', 'F', 'C']
['A', 'E', 'F', 'C', 'D']
VALID_PATH : ['A', 'E', 'F', 'C', 'D']
【问题讨论】:
【参考方案1】:Breadth-first search 遍历一个图,实际上是从一个起始节点找到所有路径。但是,通常 BFS 不会保留所有路径。相反,它更新了一个前身函数 π 以保存最短路径。您可以轻松地修改算法,以便 π(n)
不仅存储 一个 前任,而且还存储可能的前任列表。
然后所有可能的路径都被编码在这个函数中,通过递归遍历π得到所有可能的路径组合。
Cormen 等人的算法简介中可以找到使用这种表示法的一个很好的伪代码,并且随后被用于该主题的许多大学脚本中。在 Google 上搜索“BFS 伪代码前身 π”,this hit on Stack Exchange 被连根拔起。
【讨论】:
所讨论的实现是否适合广度优先搜索? 我不是 Python 专家:not in
运算符真的存在吗?除此之外,粗略一看,代码看起来还不错。不过,您可以删除 new_path = []
语句。此外,您可以在方法内创建队列并将其作为参数移除。
我只是将其转换为 c++ 并使用它。感谢您的输入
这是绝对必要的 BFS,在维护所有访问节点的列表的同时,通过递归 DFS 不能实现相同的目标吗?
@Sameer 你也可以使用 DFS,是的。【参考方案2】:
在 Prolog(特别是 SWI-Prolog)中
:- use_module(library(tabling)).
% path(+Graph,?Source,?Target,?Path)
:- table path/4.
path(_,N,N,[N]).
path(G,S,T,[S|Path]) :-
dif(S,T),
member(S-I, G), % directed graph
path(G,I,T,Path).
测试:
paths :- Graph =
[ 1- 2 % node 1 and 2 are connected
, 2- 3
, 2- 5
, 4- 2
, 5-11
,11-12
, 6- 7
, 5- 6
, 3- 6
, 6- 8
, 8-10
, 8- 9
],
findall(Path, path(Graph,1,7,Path), Paths),
maplist(writeln, Paths).
?- paths.
[1,2,3,6,7]
[1,2,5,6,7]
true.
【讨论】:
【参考方案3】:给定邻接矩阵:
0, 1, 3, 4, 0, 0
0, 0, 2, 1, 2, 0
0, 1, 0, 3, 0, 0
0, 1, 1, 0, 0, 1
0, 0, 0, 0, 0, 6
0, 1, 0, 1, 0, 0
以下 Wolfram Mathematica 代码解决了查找图的两个节点之间的所有简单路径的问题。 我使用了简单的递归和两个全局变量来跟踪循环并存储所需的输出。 代码并没有仅仅为了代码清晰而优化。 “打印”应该有助于阐明它是如何工作的。
cycleQ[l_]:=If[Length[DeleteDuplicates[l]] == Length[l], False, True];
getNode[matrix_, node_]:=Complement[Range[Length[matrix]],Flatten[Position[matrix[[node]], 0]]];
builtTree[node_, matrix_]:=Block[nodes, posAndNodes, root, pos,
If[node != && node != endNode ,
root = node;
nodes = getNode[matrix, node];
(*Print["root:",root,"---nodes:",nodes];*)
AppendTo[lcycle, Flatten[root, nodes]];
If[cycleQ[lcycle] == True,
lcycle = Most[lcycle]; appendToTree[root, nodes];,
Print["paths: ", tree, "\n", "root:", root, "---nodes:",nodes];
appendToTree[root, nodes];
];
];
appendToTree[root_, nodes_] := Block[pos, toAdd,
pos = Flatten[Position[tree[[All, -1]], root]];
For[i = 1, i <= Length[pos], i++,
toAdd = Flatten[Thread[tree[[pos[[i]]]], #]] & /@ nodes;
(* check cycles!*)
If[cycleQ[#] != True, AppendTo[tree, #]] & /@ toAdd;
];
tree = Delete[tree, # & /@ pos];
builtTree[#, matrix] & /@ Union[tree[[All, -1]]];
];
];
调用代码: 初始化节点 = 1; 结束节点 = 6; lcycle = ; 树 = initNode; builtTree[initNode, 矩阵];
路径:1 根:1---节点:2,3,4
路径:1,2,1,3,1,4 根:2---节点:3,4,5
路径:1,3,1,4,1,2,3,1,2,4,1,2,5 根:3---节点:2,4
路径:1,4,1,2,4,1,2,5,1,3,4,1,2,3,4,1,3 ,2,4,1,3,2,5 根:4---节点:2,3,6
路径:1,2,5,1,3,2,5,1,4,6,1,2,4,6,1,3,4,6 ,1,2,3,4,6,1,3,2,4,6,1,4,2,5,1,3,4,2,5,1 ,4,3,2,5 根:5---节点:6
结果:1, 4, 6, 1, 2, 4, 6, 1, 2, 5, 6, 1, 3, 4, 6, 1, 2, 3 , 4, 6, 1, 3, 2, 4, 6, 1, 3, 2, 5, 6, 1, 4, 2, 5, 6, 1, 3, 4, 2 , 5, 6, 1, 4, 3, 2, 5, 6
...不幸的是,我无法上传图片以更好地显示结果:(
http://textanddatamining.blogspot.com
【讨论】:
【参考方案4】:对于那些不是 PYTHON 专家的人,C++ 中的相同代码
//@Author :Ritesh Kumar Gupta
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
vector<vector<int> >GRAPH(100);
inline void print_path(vector<int>path)
cout<<"[ ";
for(int i=0;i<path.size();++i)
cout<<path[i]<<" ";
cout<<"]"<<endl;
bool isadjacency_node_not_present_in_current_path(int node,vector<int>path)
for(int i=0;i<path.size();++i)
if(path[i]==node)
return false;
return true;
int findpaths(int source ,int target ,int totalnode,int totaledge )
vector<int>path;
path.push_back(source);
queue<vector<int> >q;
q.push(path);
while(!q.empty())
path=q.front();
q.pop();
int last_nodeof_path=path[path.size()-1];
if(last_nodeof_path==target)
cout<<"The Required path is:: ";
print_path(path);
else
print_path(path);
for(int i=0;i<GRAPH[last_nodeof_path].size();++i)
if(isadjacency_node_not_present_in_current_path(GRAPH[last_nodeof_path][i],path))
vector<int>new_path(path.begin(),path.end());
new_path.push_back(GRAPH[last_nodeof_path][i]);
q.push(new_path);
return 1;
int main()
//freopen("out.txt","w",stdout);
int T,N,M,u,v,source,target;
scanf("%d",&T);
while(T--)
printf("Enter Total Nodes & Total Edges\n");
scanf("%d%d",&N,&M);
for(int i=1;i<=M;++i)
scanf("%d%d",&u,&v);
GRAPH[u].push_back(v);
printf("(Source, target)\n");
scanf("%d%d",&source,&target);
findpaths(source,target,N,M);
//system("pause");
return 0;
/*
Input::
1
6 11
1 2
1 3
1 5
2 1
2 3
2 4
3 4
4 3
5 6
5 4
6 3
1 4
output:
[ 1 ]
[ 1 2 ]
[ 1 3 ]
[ 1 5 ]
[ 1 2 3 ]
The Required path is:: [ 1 2 4 ]
The Required path is:: [ 1 3 4 ]
[ 1 5 6 ]
The Required path is:: [ 1 5 4 ]
The Required path is:: [ 1 2 3 4 ]
[ 1 2 4 3 ]
[ 1 5 6 3 ]
[ 1 5 4 3 ]
The Required path is:: [ 1 5 6 3 4 ]
*/
【讨论】:
:你能说出上面代码的复杂度吗?? 您是否从输入文本文件中导入了此图 这个算法的时间复杂度是多少? O(n!)? 非常有帮助!竖起大拇指。【参考方案5】:原始代码有点麻烦,如果您想使用 BFS 来查找图上 2 点之间是否存在路径,您可能需要使用 collections.deque。这是我破解的一个快速解决方案:
注意:如果两个节点之间不存在路径,此方法可能会无限继续。我还没有测试所有的情况,YMMV。
from collections import deque
# a sample graph
graph = 'A': ['B', 'C','E'],
'B': ['A','C', 'D'],
'C': ['D'],
'D': ['C'],
'E': ['F','D'],
'F': ['C']
def BFS(start, end):
""" Method to determine if a pair of vertices are connected using BFS
Args:
start, end: vertices for the traversal.
Returns:
[start, v1, v2, ... end]
"""
path = []
q = deque()
q.append(start)
while len(q):
tmp_vertex = q.popleft()
if tmp_vertex not in path:
path.append(tmp_vertex)
if tmp_vertex == end:
return path
for vertex in graph[tmp_vertex]:
if vertex not in path:
q.append(vertex)
【讨论】:
【参考方案6】:Dijkstra 的算法更适用于加权路径,听起来发布者想要找到所有路径,而不仅仅是最短路径。
对于这个应用程序,我会构建一个图表(您的应用程序听起来不需要定向)并使用您最喜欢的搜索方法。听起来您想要所有路径,而不仅仅是猜测最短的路径,因此请使用您选择的简单递归算法。
唯一的问题是图是否可以是循环的。
通过连接:
1, 2 1, 3 2, 3 2, 4在寻找从 1->4 的路径时,您可能有一个 1 -> 2 -> 3 -> 1 的循环。
在这种情况下,我会保留一个堆栈作为遍历节点。这是一个包含该图的步骤和结果堆栈的列表(抱歉格式化 - 没有表格选项):
当前节点(可能的下一个节点减去我们来自哪里)[堆栈]
-
1 (2, 3) [1]
2 (3, 4) [1, 2]
3 (1) [1, 2, 3]
1 (2, 3) [1, 2, 3, 1] //错误 - 堆栈上的重复数字 - 检测到循环
3 () [1, 2, 3] // 后退到节点 3 并从堆栈中弹出 1。此处不再有可供探索的节点
2 (4) [1, 2] // 后退到节点 2 并将 1 从堆栈中弹出。
4 () [1, 2, 4] // 找到目标节点 - 路径的记录堆栈。此处不再有可供探索的节点
2 () [1, 2] //后退到节点 2 并从堆栈中弹出 4。此处不再有可供探索的节点
1 (3) [1] //后退到节点 1 并将 2 从堆栈中弹出。
3 (2) [1, 3]
2 (1, 4) [1, 3, 2]
1 (2, 3) [1, 3, 2, 1] //错误 - 堆栈上的重复数字 - 检测到循环
2 (4) [1, 3, 2] //后退到节点2并从堆栈中弹出1
4 () [1, 3, 2, 4] 找到目标节点 - 路径的记录堆栈。此处不再有可供探索的节点
2 () [1, 3, 2] //后退到节点 2 并从堆栈中弹出 4。没有更多节点
3 () [1, 3] // 后退到节点 3 并将 2 从堆栈中弹出。没有更多节点
1 () [1] // 后退到节点 1 并将 3 从堆栈中弹出。没有更多节点
使用 [1, 2, 4] 和 [1, 3, 2, 4] 的 2 条记录路径完成
【讨论】:
【参考方案7】:如果您想要所有路径,请使用递归。
使用邻接列表,最好创建一个函数 f() 来尝试填充当前访问顶点列表。像这样:
void allPaths(vector<int> previous, int current, int destination)
previous.push_back(current);
if (current == destination)
//output all elements of previous, and return
for (int i = 0; i < neighbors[current].size(); i++)
allPaths(previous, neighbors[current][i], destination);
int main()
//...input
allPaths(vector<int>(), start, end);
由于向量是按值传递的(因此在递归过程中进一步进行的任何更改都不是永久性的),因此会枚举所有可能的组合。
您可以通过引用传递 previous 向量来提高效率(因此不需要一遍又一遍地复制向量),但您必须确保事情得到 popped_back () 手动。
还有一件事:如果图形有循环,这将不起作用。 (我假设在这种情况下你会想要找到所有 simple 路径,然后)在向 previous 向量中添加一些东西之前,首先检查它是否已经存在。 p>
如果您想要所有 最短 路径,请使用 Konrad 对这个算法的建议。
【讨论】:
这会输出每条路径,而不仅仅是每条简单的路径。对于无向图或循环有向图,代码将创建长度增加的路径,最终导致调用堆栈溢出。它应该检查当前是否在以前,如果是则停止递归。【参考方案8】:您要做的基本上是在(有向?)图中找到两个顶点之间的路径,如果您需要最短路径,请查看Dijkstra's algorithm,或者如果您需要任何存在的路径,请编写一个简单的递归函数。
【讨论】:
你能在这里添加示例代码并解释如何使用递归函数 他不需要最短路径,他需要“找到两个给定节点之间的路径”。以上是关于找到两个给定节点之间的路径?的主要内容,如果未能解决你的问题,请参考以下文章