给定一个图和一个加权无向图中的源顶点 src ,找到从 src 到给定图中所有顶点的最短路径。该图可能包含负权重边。
对于这个问题,我们已经讨论过 Dijkstra 算法和 Bellman-Ford 算法。但是 D’Esopo-Pape 算法在大多数情况下都表现得很好。然而,在某些情况下,它需要指数时间。
推荐:请先在“ PRACTICE ”上解决,然后再继续解决。
算法:
输入:图的邻接表和源顶点src。
输出:从 src 到所有顶点的最短距离。
该算法使用双向队列来存储要操作的顶点。
下面是算法的详细步骤。
- 初始化数组中顶点从源到无穷大的距离。
- 维护一个队列,该队列将存储要操作的顶点,并维护一个顶点的布尔数组,用于确定顶点是否已经存在于队列中。
- 在队列中追加源顶点。
- 开始从队列中弹出顶点直到队列为空,并对每个弹出的顶点执行以下步骤(让U为弹出的顶点):
- 将顶点U设置为队列中不存在。
- 对于 U 的每个相邻顶点V ,检查其当前最小距离 [V]是否大于通过U的距离,
即距离[U] + 连接 U 和 V 的边的权重。 - 如果是,更新距离 [V] = 距离 [U] + 连接 U 和 V 的边的权重。
借助布尔数组检查队列中是否不存在 V:- 如果V是第一次进入队列,则将 V 附加到队列的后面,并借助布尔数组将顶点 V 设置为存在于队列中。
- 否则追加到队列的前面,并将 V 顶点设置为队列中存在。
- 返回列表距离,每个顶点到源顶点的距离最短。
例如:
最初,从源到自身的距离将为 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 现场工作专业课程和学生竞争性编程现场课程。