Найти пути между двумя заданными узлами?

голоса
42

Скажем, у меня есть узлы, соединенные ниже способ, как я приезжаю в числе путей, которые существуют между заданными точками, и деталью путем?

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

альтернативный

реализация нашла здесь хорошо , я буду использовать тот же

Вот отрывок из приведенной выше ссылки в питоне

# 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']
Задан 03/04/2009 в 12:09
источник пользователем
На других языках...                            


8 ответов

голоса
-3

То , что вы пытаетесь сделать , это , по существу , чтобы найти путь между двумя вершинами в (направлено?) Графе проверить алгоритм Дейкстров , если вам нужен кратчайший путь или написать простую рекурсивную функцию , если вам нужно то , что существуют пути.

Ответил 03/04/2009 в 12:14
источник пользователем

голоса
33

Поиск в ширину пересекает график и фактически находит все пути от исходного узла. Обычно BFS не сохраняет все пути, однако. Вместо этого, он обновляет функцию prededecessor тг , чтобы сохранить кратчайший путь. Вы можете легко изменить алгоритм так , чтобы π(n)не только сохранить один предшественник , но список возможных предшественников.

Тогда все возможные пути закодированы в этой функции, и путь обхода П рекурсивно вы получите все возможные комбинации пути.

Один хороший псевдокод , который использует эти обозначения можно найти в Введении в Алгоритмы на Cormen и др. и впоследствии был использован во многих сценариях университета по данной теме. Поиск Google для «BFS псевдокод предшественник π» выкорчёвывает этот хит на Stack бирже .

Ответил 03/04/2009 в 12:38
источник пользователем

голоса
1

Если вы хотите, чтобы все пути, использовать рекурсию.

Использование списка смежности, предпочтительно, создать функцию 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 () вручную.

Еще одна вещь: если граф имеет циклы, это не будет работать. (Я предполагаю , что в этом случае вы будете хотеть , чтобы найти все простые пути, то) Прежде чем добавить что - то в предыдущий вектор, первая проверка , если она уже там.

Если вы хотите , чтобы все кратчайшие пути, используйте предложение Конрада с этим алгоритмом.

Ответил 03/04/2009 в 12:45
источник пользователем

голоса
7

Алгоритм Дейкстров больше относится к взвешенным путям и это звучит как плакат хочет, чтобы найти все пути, а не только самую короткий.

Для этого приложения, я бы построить график (приложение кажется, что не нужно будет направлять) и использовать ваш любимый метод поиска. Это звучит, как вы хотите, чтобы все пути, а не просто догадка на самый короткий, поэтому использовать простой рекурсивный алгоритм вашего выбора.

Единственная проблема с этим, если граф может быть циклическим.

С соединениями:

  • 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] // Back-ступенчатым к узлу три и выскочил один из стека. Нет больше узлов , чтобы исследовать здесь
  6. 2 (4) [1, 2] // спин подошел к узлу 2 , и выскочил один из стека.
  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. Совершенно с 2 записанных путями [1, 2, 4] и [1, 3, 2, 4]
Ответил 03/04/2009 в 12:52
источник пользователем

голоса
3

Исходный код является немного громоздким, и вы можете использовать collections.deque вместо этого, если вы хотите использовать BFS, чтобы найти, если существует путь между 2 точками на графике. Вот быстрое решение, которое я изрубил:

Примечание: этот метод может продолжаться бесконечно, если не существует пути между двумя узлами. Я не проверял все случаи, 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)
Ответил 20/07/2009 в 03:22
источник пользователем

голоса
22

Для тех, кто не ПИТОН эксперт, тот же самый код в 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 ]


*/
Ответил 04/06/2012 в 20:17
источник пользователем

голоса
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; endNode = 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

Ответил 23/08/2012 в 19:58
источник пользователем

голоса
3

В Прологе (в частности, SWI-Пролог)

:- 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.
Ответил 15/09/2016 в 12:02
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more