如何修改Dijkstra算法以查找所有可能的路径?

11

我知道可能之前已经有人问过,但我找不到答案。 我需要修改下面的Dijkstra算法,它能够很好地找出2个节点之间的最短路径,但我还需要找到所有可能的路径。 我知道这应该相对容易做到,但到目前为止,我还不知道如何以最简单的方式做到这一点。我正在使用有向加权图。

    class Dijkstra
    {
        private List<Node> _nodes;
        private List<Edge> _edges;
        private List<Node> _basis;
        private Dictionary<string, double> _dist;
        private Dictionary<string, Node> _previous;

        public Dijkstra(List<Edge> edges, List<Node> nodes)
        {

            Edges = edges;
            Nodes = nodes;
            Basis = new List<Node>();
            Dist = new Dictionary<string, double>();
            Previous = new Dictionary<string, Node>();

            // record node 
            foreach (Node n in Nodes)
            {
                Previous.Add(n.Name, null);
                Basis.Add(n);
                Dist.Add(n.Name, double.MaxValue);
            }
        }

        /// Calculates the shortest path from the start
        ///  to all other nodes
        public void calculateDistance(Node start)
        {
            Dist[start.Name] = 0;

            while (Basis.Count > 0)
            {
                Node u = getNodeWithSmallestDistance();
                if (u == null)
                {
                    Basis.Clear();
                }
                else
                {
                    foreach (Node v in getNeighbors(u))
                    {
                        double alt = Dist[u.Name] + getDistanceBetween(u, v);
                        if (alt < Dist[v.Name])
                        {
                            Dist[v.Name] = alt;
                            Previous[v.Name] = u;
                        }
                    }
                    Basis.Remove(u);
                }
            }
        }

        public List<Node> getPathTo(Node d)
        {
            List<Node> path = new List<Node>();

            path.Insert(0, d);

            while (Previous[d.Name] != null)
            {
                d = Previous[d.Name];
                path.Insert(0, d);
            }

            return path;
        }

    public Node getNodeWithSmallestDistance()
        {
            double distance = double.MaxValue;
            Node smallest = null;

            foreach (Node n in Basis)
            {
                if (Dist[n.Name] < distance)       
                {
                    distance = Dist[n.Name];
                    smallest = n;
                }
            }

            return smallest;
        }


        public List<Node> getNeighbors(Node n)
        {
            List<Node> neighbors = new List<Node>();

            foreach (Edge e in Edges)
            {
                if (e.Origin.Equals(n) && Basis.Contains(n))
                {
                    neighbors.Add(e.Destination);
                }
            }

            return neighbors;
        }

       public double getDistanceBetween(Node o, Node d)
        {
            foreach (Edge e in Edges)
            {
                if (e.Origin.Equals(o) && e.Destination.Equals(d))
                {
                    return e.Distance;
                }
            }

            return 0;
        }


        public List<Node> Nodes
        {
            get { return _nodes; }
            set { _nodes = value; }
        }

        public List<Edge> Edges
        {
            get { return _edges; }
            set { _edges = value; }
        }

        public List<Node> Basis
        {
            get { return _basis; }
            set { _basis = value; }
        }

        public Dictionary<string, double> Dist
        {
            get { return _dist; }
            set { _dist = value; }
        }

        public Dictionary<string, Node> Previous
        {
            get { return _previous; }
            set { _previous = value; }
        }
    }
}

static void Main(string[] args)
        {
//Nodes initialisation goes here

Dijkstra d = new Dijkstra(_edges, _nodes);
d.calculateDistance(_dictNodes["A"]);
 List<Node> path = d.getPathTo(_dictNodes["C"]);
}

我已经编辑了你的标题。请参考“问题的标题应该包含“标签”吗?”,在那里达成共识是“不应该”。 - John Saunders
你知道这个算法是如何工作的吗?一旦你知道了,就更容易找到如何改变它,以返回所有可能的路径。 - Bartlomiej Lewandowski
5个回答

4

好的,我已经修改了Dijkstra类,使其能够执行BFS,并且可以获取到所有可能的路线。我添加了这个方法:

public void BreadthFirst(Edge graph, LinkedList<String> visited) 
{
    LinkedList<String> nodes = graph.adjacentNodes(visited.Last());

    // Examine adjacent nodes
    foreach (String node in nodes) 
    {
        if (visited.Contains(node))
        {
            continue;
        }

        if (node.Equals(endNode))
        {
            visited.AddLast(node);

            printPath(visited);

            visited.RemoveLast();

            break;
         }
     }

    // In breadth-first, recursion needs to come after visiting adjacent nodes
    foreach (String node in nodes)
    {      
        if(visited.Contains(node) || node.Equals(endNode))
        {
            continue;
        }

        visited.AddLast(node);

        // Recursion
        BreadthFirst(graph, visited);

        visited.RemoveLast();
    }
}

使用方法如下:

Dijkstra d = new Dijkstra(_edges, _nodes);

LinkedList<String> visited = new LinkedList<string>();  //collecting all routes
visited.AddFirst(start);

d.BreadthFirst(graph, visited);

你帮了我很多,Zulu。我能够将你的代码适应到我的现有代码需求中。非常感谢! :) 继续分享吧... - Leniel Maccaferri
我在评论一个非常古老的帖子,但有一个快速的基本问题。在您修改后的新方法BreadthFirst中,图变量是否保存邻接列表节点?如果我理解正确,那么graph.adjacentNodes(visited.Last())将简单地给出与当前节点相邻的所有节点,如邻接列表表示所提供的那样。如果是这样,那么这个方法从Dijkstra算法中获得了什么?如果我没有忽略什么,输入似乎是邻接图而不是Dijkstra的输出? - Saurabh Kumar

2

你不能轻易地修改 Dijkstra 算法以显示所有可能的路径。你需要修改 BFS 或 DFS 搜索算法。

如果你试图修改 Dijkstra 算法,最终你将会得到一个 BFS 或 DFS 搜索算法,因此最好从那里开始。


在下面的代码部分中,我已经列出了Dist[]中所有可能的邻居路线,但程序当然会选择最短的路线,所以我想也许有一些聪明的技巧可以完成这项工作。public Node getNodeWithSmallestDistance() { double distance = double.MaxValue; Node smallest = null; foreach (Node n in Basis) { if (Dist[n.Name] < distance)
{ distance = Dist[n.Name]; smallest = n; } } return smallest;
- Zulu Z

1
如果您想找到所有的简单路径,则使用修改后的BFS(您将记住已使用的顶点,以便在路径中不重复)。查找所有路径甚至可能是不可能的(该过程将不会终止(即它不是算法))。想象一下带有循环的图,所有节点之间都有无限多的路径(包含不同数量的循环)。

0

这里有几个我在网上找到的算法,用于查找图中所有可能的路径。它们不会修改Dijkstra算法,但我认为它们应该能够满足您的需求。


来自https://mathoverflow.net/questions/18603/finding-all-paths-on-undirected-graph

Suresh建议使用DFS,MRA指出这并不清楚是否有效。以下是我尝试根据评论线索解决问题的方法。如果图形具有m条边,n个节点和从源s到目标t的p条路径,则下面的算法在时间O((np + 1)(m + n))内打印所有路径。 (特别地,花费O(m + n)的时间来注意到没有路径。)

思路非常简单:进行详尽的搜索,但如果你陷入了困境,请尽早退出。

如果不提前退出,MRA的反例表明即使p = 1,详尽的搜索也需要Ω(n!)的时间:节点t只有一个相邻的边缘,其邻居是节点s,它是完整的(子)图Kn−1的一部分。

将s推入路径堆栈并调用search(s):

path // is a stack (initially empty)
seen // is a set

def stuck(x)
   if x == t
     return False
   for each neighbor y of x
     if y not in seen
       insert y in seen
       if !stuck(y)
         return False
   return True

def search(x)
  if x == t
    print path
  seen = set(path)
  if stuck(x)
    return
  for each neighbor y of x
    push y on the path
    search(y)
    pop y from the path

这里的搜索是全面搜索,而卡住的情况可以采用深度优先搜索(如此处)或广度优先搜索实现。

来自循环无向图中所有可能的路径

您可以像|Vlad描述的那样使用DFS找到所有路径。要找出在每个路径中出现的节点,您可以维护一个布尔数组,该数组指示每个节点到目前为止是否出现在每个路径中。当您的DFS找到一条路径时,请遍历不在路径中的每个顶点并将相应的数组值设置为false。完成后,只有具有true值的顶点才是出现在每个路径中的顶点。

伪代码:

int source;
int sink;
int nVerts;
bool inAllPaths[nVerts]; // init to all true
bool visited[nVerts]; // init to all false
stack<int> path; // init empty

bool dfs(int u)
  if (visited[u])
    return;
  if (u == sink)
    for i = 0 to nVerts-1
      if !stack.contains(i)
        inAllPaths[i] = false;
    return true;
  else
    visited[u] = true;
    stack.push(u);
    foreach edge (u, v)
      dfs(v);
    stack.pop();
    visited[u] = false;
    return false;


main()
  dfs(source);
  // inAllPaths contains true at vertices that exist in all paths
  // from source to sink.

然而,这个算法并不是很高效。例如,在一个完全图中,有n个顶点(每个顶点都与其他所有顶点相连),路径的数量将会是n!(n的阶乘)。
一个更好的算法是分别检查每个顶点在每条路径中的存在性。对于每个顶点,尝试找到一条从源点到汇点的路径,但不经过该顶点。如果找不到这样的路径,那是因为该顶点出现在每条路径中。
伪代码:
// Using the same initialisation as above, but with a slight modification
// to dfs: change the foreach loop to
foreach edge (u, v)
  if (dfs(v))
    return true; // exit as soon as we find a path

main()
  for i = 0 to nVerts-1
    set all visited to false;
    if (inAllPaths[i])
      visited[i] = true;
      if (dfs(source))
        inAllPaths[i] = false;
      visited[i] = false;

很遗憾,当查找路径时,这仍然具有指数最坏情况。您可以通过将搜索更改为广度优先搜索来解决此问题。如果我没记错,这应该给您O(VE)的性能。

一些讨论此问题的其他文章:

枚举所有可能路径的算法
查找两个图节点之间的所有路径


0

以下是如何使用 BFS 实现的:可以使用以下(python)函数(修改了两个节点之间的 递归路径查找 函数的 BFS),以查找 无环图 中两个节点之间的所有可能路径:

from collections import defaultdict

# modified BFS
def find_all_parents(G, s):
    Q = [s]
    parents = defaultdict(set)
    while len(Q) != 0:
        v = Q[0]
        Q.pop(0)
        for w in G.get(v, []):
            parents[w].add(v)
            Q.append(w) 
    return parents

# recursive path-finding function (assumes that there exists a path in G from a to b)   
def find_all_paths(parents, a, b): 
    return [a] if a == b else [y + b for x in list(parents[b]) for y in find_all_paths(parents, a, x)]

例如,给定以下有向无环图(DAG)G:
G = {'A':['B','C'], 'B':['D'], 'C':['D', 'F'], 'D':['E', 'F'], 'E':['F']}

如果我们想要找到节点'A''F'之间的所有路径(使用上述定义的函数find_all_paths(find_all_parents(G, 'A'), 'A', 'F')),它将返回以下路径:

enter image description here


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