48

假设我有按以下方式连接的节点,我如何得出给定点之间存在的路径数量以及路径详细信息?

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

替代文字

在这里找到的实现很好,我将使用相同的

这是python中上述链接的片段

# 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']
4

8 回答 8

34

广度优先搜索遍历一个图,实际上是从一个起始节点找到所有路径。但是,通常 BFS 不会保留所有路径。相反,它更新了一个前身函数 π 以保存最短路径。您可以轻松地修改算法,以便π(n)不仅存储一个前任,而且存储可能的前任列表。

然后所有可能的路径都被编码在这个函数中,并且通过递归地遍历 π 你得到所有可能的路径组合。

在Cormen等人的《算法导论》中可以找到使用这种表示法的一个很好的伪代码。并随后被用于该主题的许多大学脚本中。谷歌搜索“BFS 伪代码前身 π”在 Stack Exchange 上根除了这个命中

于 2009-04-03T11:38:44.873 回答
23

对于那些不是 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 ]


*/
于 2012-06-04T19:17:45.283 回答
7

Dijkstra 的算法更适用于加权路径,听起来发布者想要找到所有路径,而不仅仅是最短路径。

对于这个应用程序,我将构建一个图表(您的应用程序听起来不需要定向)并使用您最喜欢的搜索方法。听起来您想要所有路径,而不仅仅是猜测最短的路径,因此请使用您选择的简单递归算法。

唯一的问题是图形是否可以是循环的。

通过连接:

  • 1, 2
  • 1、3
  • 2、3
  • 2、4

在寻找从 1->4 的路径时,您可能有一个 1 -> 2 -> 3 -> 1 的循环。

在这种情况下,我会保留一个堆栈作为遍历节点。这是一个包含该图的步骤和结果堆栈的列表(抱歉格式化 - 没有表格选项):

当前节点(可能的下一个节点减去我们来自哪里)[堆栈]

  1. 1 (2, 3) [1]
  2. 2 (3, 4) [1, 2]
  3. 3 (1) [1, 2, 3]
  4. 1 (2, 3) [1, 2, 3, 1] //错误 - 堆栈上的重复数字 - 检测到循环
  5. 3 () [1, 2, 3] // 后退到节点 3 并将 1 从堆栈中弹出。没有更多的节点可以从这里探索
  6. 2 (4) [1, 2] // 后退到节点 2 并将 1 从堆栈中弹出。
  7. 4 () [1, 2, 4] // 找到目标节点 - 路径的记录堆栈。没有更多的节点可以从这里探索
  8. 2 () [1, 2] //后退到节点 2 并从堆栈中弹出 4。没有更多的节点可以从这里探索
  9. 1 (3) [1] //后退到节点 1 并将 2 从堆栈中弹出。
  10. 3 (2) [1, 3]
  11. 2 (1, 4) [1, 3, 2]
  12. 1 (2, 3) [1, 3, 2, 1] //错误 - 堆栈上的重复数字 - 检测到循环
  13. 2 (4) [1, 3, 2] //后退到节点 2 并从堆栈中弹出 1
  14. 4 () [1, 3, 2, 4] 找到目标节点 - 路径的记录堆栈。没有更多的节点可以从这里探索
  15. 2 () [1, 3, 2] //后退到节点 2 并从堆栈中弹出 4。没有更多节点
  16. 3 () [1, 3] // 后退到节点 3 并将 2 从堆栈中弹出。没有更多节点
  17. 1 () [1] // 后退到节点 1 并将 3 从堆栈中弹出。没有更多节点
  18. 用 [1, 2, 4] 和 [1, 3, 2, 4] 的 2 条记录路径完成
于 2009-04-03T11:52:46.667 回答
3

原始代码有点麻烦,如果您想使用 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)
于 2009-07-20T02:22:14.657 回答
3

在 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.
于 2016-09-15T12:02:02.813 回答
2

如果您想要所有路径,请使用递归。

使用邻接表,最好创建一个函数 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);
}

由于向量是按值传递的(因此在递归过程中进一步向下进行的任何更改都不是永久性的),因此会枚举所有可能的组合。

您可以通过引用传递前一个向量来提高效率(因此不需要一遍又一遍地复制向量),但您必须确保事情得到 popped_back() 手动。

还有一件事:如果图表有循环,这将不起作用。(我假设在这种情况下你会想要找到所有简单的路径,然后)在向前一个向量添加一些东西之前,首先检查它是否已经在那里。

如果您想要所有最短路径,请使用此算法的 Konrad 建议。

于 2009-04-03T11:45:42.673 回答
2

给定邻接矩阵:

{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]]];
    ];
];

调用代码:initNode = 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

于 2012-08-23T18:58:00.330 回答
-4

您要做的基本上是在(有向?)图中找到两个顶点之间的路径,如果您需要最短路径,请查看Dijkstra 算法,或者如果您需要任何存在的路径,请编写一个简单的递归函数。

于 2009-04-03T11:14:39.363 回答