📌  相关文章
📜  D’Esopo-Pape算法:单源最短路径

📅  最后修改于: 2021-04-26 08:43:21             🧑  作者: Mango

给定一个图和一个加权无向图中的源顶点src ,找到从src到给定图中所有顶点的最短路径。该图可能包含负权重边缘。

对于这个问题,我们已经讨论了Dijkstra算法和Bellman-Ford算法。但是D’Esopo-Pape算法在大多数情况下的效果都很好。但是,在某些情况下会花费指数时间。

建议:在继续解决方案之前,请先在“实践”上解决它。

算法:
输入:图形和源顶点src的邻接列表。
输出:距src的所有顶点的最短距离。
该算法使用双向队列来存储要操作的顶点。

以下是该算法的详细步骤。

  1. 初始化顶点从源到无限的阵列中的顶点的距离。
  2. 维护一个队列,该队列将存储要操作的顶点,还维护一个用于顶点的布尔数组,该数组将用于确定顶点是否已存在于队列中。
  3. 在队列中附加源顶点。
  4. 从队列开始弹出顶点,直到队列为空,并对每个弹出的顶点执行以下步骤(让U为弹出的顶点):
    • 将顶点U设置为不存在于队列中。
    • 对于U的每个相邻顶点V ,检查其当前的最小Distance [V]是否大于通过U的距离,
      距离[U] +连接U和V的边的权重
    • 如果是,则更新距离[V] =距离[U] +连接U和V的边的权重。
      借助布尔数组检查队列中是否不存在V:
      1. 如果V是第一次进入队列,请在队列的后面附加V并在布尔数组的帮助下将顶点V设置为队列中存在的值。
      2. 否则,将其追加到队列的最前面,并将V顶点设置为队列中存在的值。
  5. 返回列表距离每个顶点到源顶点的最短距离。

例如:
最初,从源到其自身的距离将为0,而对于其他顶点,该距离将是无限的。

现在,对于这种情况下为0的源的每个相邻顶点,[1,4]更新距离并将顶点分别标记为,权重分别为4和8。

现在从队列中取出顶点4 ,然后将相邻的顶点连接到顶点4 –

  • 顶点1 –由于顶点1已经访问并且到达顶点1的权重为4,而当从源通过边4_1移到顶点1时,总权重将为11,这大于距离数组中存储的权重。
  • 顶点3 –由于未访问顶点3且队列中也不存在该顶点3,因此顶点3的距离已更新为9,并且也排入了队列的最前面。

同样,从队列中取出顶点3,并更新相邻顶点的值。顶点3的相邻顶点是顶点4和顶点2。

  • 顶点4 –由于已经访问了顶点4并且权重已经最小,因此不会更新距离。
  • 顶点2 –由于未访问顶点2且队列中也没有该顶点2,因此顶点3的距离已更新为11,并且也排入了队列的最前面。

下面是上述方法的实现。

C++
// C++ implementation for
// D'Esopo-Pape algorithm
#include 
using namespace std;
#define inf INT_MAX
 
vector desopo(vector> &graph)
{
   
  // Number of vertices in graph
  int v = graph.size();
 
  // Adjacency list of graph
  map>> adj;
  for(int i = 0; i < v; i++) {
    for(int j = i + 1; j < v; j++)
    {
      if (graph[i][j] != 0)
      {
        adj[i].push_back({graph[i][j], j});
        adj[j].push_back({graph[i][j], i});
      }
    }
  }
 
  // Queue to store unoperated vertices
  deque q;
 
  // Distance from source vertex
  // distance =[float('inf')]*v
  vector distance(v, inf);
 
  // Status of vertex
  vector is_in_queue(v, false);
 
  // let 0 be the source vertex
  int source = 0;
  distance = 0;
  q.push_back(source);
  is_in_queue = true;
 
  while (!q.empty())
  {
     
    // Pop from front of the queue
    int u = q.front();
    q.pop_front();
    is_in_queue[u] = false;
 
    // Scan adjacent vertices of u
    for(auto e : adj[u])
    {
       
      // e <- [weight, vertex]
      if (distance[e.second] >
          distance[u] + e.first)
      {
        distance[e.second] = distance[u] + e.first;
        if (!is_in_queue[e.second])
        {
           
          // if e.second is entering
          // first time in the queue
          if (distance[e.second] == inf)
             
            // Append at back of queue
            q.push_back(e.second);
          else
             
            // Append at front of queue
            q.push_front(e.second);
           
          is_in_queue[e.second] = true;
        }
      }
    }
  }
  return distance;
}
 
// Driver Code
int main(int argc, char const *argv[])
{
   
  // Adjacency matrix of graph
  vector> graph = { { 0, 4, 0, 0, 8 },
                                { 0, 0, 8, 0, 11 },
                                { 0, 8, 0, 2, 0 },
                                { 0, 0, 2, 0, 1 },
                                { 8, 11, 0, 1, 0 } };
  for(auto i : desopo(graph))
  {
    cout << i << " ";
  }
  return 0;
}
 
// This code is contributed by sanjeev2552


Python3
# Python3 implementation for
# D'Esopo-Pape algorithm
 
from collections import defaultdict, deque
 
def desopo(graph):
    # Number of vertices in graph
    v = len(graph)
     
    # Adjacency list of graph
    adj = defaultdict(list)
    for i in range(v):
        for j in range(i + 1, v):
            if graph[i][j] != 0:
                adj[i].append(
                    [graph[i][j], j]
                )
                adj[j].append(
                    [graph[i][j], i]
                )
     
     
    # Queue to store unoperated vertices
    q = deque([])
     
    # Distance from source vertex
    distance =[float('inf')]*v
     
    # Status of vertex
    is_in_queue =[False]*v
     
    # let 0 be the source vertex
    source = 0
    distance= 0
    q.append(source)
    is_in_queue= True
     
    while q:
        # Pop from front of the queue
        u = q.popleft()
        is_in_queue[u]= False
     
        # scan adjacent vertices of u
        for e in adj[u]:
            # e <- [weight, vertex]
            if distance[e[1]] > distance[u]+e[0]:
                distance[e[1]]= distance[u]+e[0]
                if is_in_queue[e[1]]== False:
                    # if e[1] is entering
                    # first time in the queue
                    if distance[e[1]]== float('inf'):
                        # Append at back of queue
                        q.append(e[1])
                    else:
                        # Append at front of queue
                        q.appendleft(e[1])
                    is_in_queue[e[1]] = True
    return distance
 
 
# Driver Code
if __name__ == "__main__":
    # Adjacency matrix of graph
    graph = [[0, 4, 0, 0, 8],
            [0, 0, 8, 0, 11],
            [0, 8, 0, 2, 0],
            [0, 0, 2, 0, 1],
            [8, 11, 0, 1, 0]
            ]
    print(desopo(graph))


输出:
[0, 4, 11, 9, 8]