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

时间:2013-04-14 18:55:57

标签: c# algorithm dijkstra

我知道以前可以问过,但我找不到它。 我需要修改下面的dijkstra算法,它适用于查找 两个节点之间的最短路径,但我也需要找到所有可能的路径。 我知道这样做应该相对容易,但到目前为止我还没有想法 怎么做这个最简单的方法。我正在使用有向加权图。

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

5 个答案:

答案 0 :(得分:3)

好的,我实际上已经修改了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);

答案 1 :(得分:2)

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

如果你试图修改Dijkstra,最后你将以BFS或DFS搜索算法结束,所以最好从那里开始。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

以下是我在网上找到的一些算法,用于查找图表中的所有可能路径。他们不会修改Dijkstra的算法,但我认为他们应该做你想做的事。


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

   Suresh建议DFS,MRA指出,目前尚不清楚是否有效。这是我尝试在该评论主题之后的解决方案。如果图形具有从源s到目标t的m个边,n个节点和p个路径,则下面的算法以时间O((np + 1)(m + n))打印所有路径。 (特别是,需要O(m + n)时间才能注意到没有路径。)

     

这个想法非常简单:进行详尽的搜索,但如果你已经陷入困境,请尽早保释。

     

如果没有提前退出,MRA的反例表明,即使p = 1,穷举搜索也花费Ω(n!)时间:节点t只有一个相邻边,而它的邻居是节点s,这是完整的一部分( sub)图Kn-1。

     

按下路径堆栈并调用搜索:

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
  

此处搜索可以在DFS样式(如此处)或BFS样式中实现穷举搜索和卡住。


来自All possible paths in a cyclic undirected graph

  

您可以使用DFS找到所有路径,例如| Vlad描述。要找到每个路径中出现的节点,您可以只维护一个布尔数组,说明到目前为止每个节点是否都出现在每个路径中。当您的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)表现。


其他一些文章讨论了这个问题:

algorithm to enumerate all possible paths
Find all paths between two graph nodes

答案 4 :(得分:0)

这是使用 BFS 的方法:以下(GridLength)函数(使用递归路径查找修改的 BFS < / em>两个节点之间的函数)可用于查找非循环图中的两个节点之间的所有可能路径:

python

例如,下图(DAG)G由给出

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)]

如果我们要查找节点G = {'A':['B','C'], 'B':['D'], 'C':['D', 'F'], 'D':['E', 'F'], 'E':['F']} 'A'之间的所有路径(使用上面定义的函数作为'F',它将返回以下路径:

enter image description here