📜  D'Esopo-Pape 算法:单源最短路径

📅  最后修改于: 2021-10-25 03:28:45             🧑  作者: Mango

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

对于这个问题,我们已经讨论过 Dijkstra 算法和 Bellman-Ford 算法。但是 D’Esopo-Pape 算法在大多数情况下都表现得很好。然而,在某些情况下,它需要指数时间。

推荐:请先在“ PRACTICE ”上解决,然后再继续解决。

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

下面是算法的详细步骤。

  1. 初始化数组中顶点从源到无穷大的距离。
  2. 维护一个队列,该队列将存储要操作的顶点,并维护一个顶点的布尔数组,用于确定顶点是否已经存在于队列中。
  3. 在队列中追加源顶点。
  4. 开始从队列中弹出顶点直到队列为空,并对每个弹出的顶点执行以下步骤(让U为弹出的顶点):
    • 将顶点U设置为队列中不存在。
    • 对于 U 的每个相邻顶点V ,检查其当前最小距离 [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 的距离更新为 9,并且也排入队列的最前面。

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

  • 顶点 4 – 由于顶点 4 已经被访问并且权重已经最小,所以距离没有更新。
  • 顶点 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]

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程。