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

5 回答 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);
于 2013-04-20T13:42:27.853 回答
2

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

如果您尝试修改 Dijkstra,最终会以 BFS 或 DFS 搜索算法结束,因此最好从那里开始。

于 2013-04-14T20:08:35.550 回答
1

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

于 2013-04-15T13:40:21.160 回答
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

这里的搜索进行了详尽的搜索,并且可以以 DFS 样式(如这里)或 BFS 样式实现卡住。


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

您可以使用 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) 性能。


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

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

于 2013-04-14T19:24:47.080 回答
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'(使用上面定义的函数 as find_all_paths(find_all_parents(G, 'A'), 'A', 'F')),它将返回以下路径:

在此处输入图像描述

于 2020-01-06T09:24:14.143 回答