Bellman-ford algorithm
Bellman-ford is an easy-to-understand single-source shortest path algorithm, and the Bellman-ford algorithm requires two arrays to assist:
dis[i]
: Store vertex i to source point known shortest path
path[i]
: Stores vertex I to the previous vertex of the known shortest path on the source point, I.
If the graph has n vertices, the longest simple path length in the graph does not exceed n-1, so the n-1 iteration of the Ford algorithm ensures that the shortest path is obtained.
Each iteration of the Ford algorithm traverses all edges and relaxes (relax) the edges. Relaxation on edge e refers to updating the known shortest path if the path that reaches E.stop from the source point is less than the known shortest path by E.start.
To facilitate the description, this paper uses the Python implementation algorithm. Two tool functions are implemented first:
INF = 1e6def make_mat(m, n, fill=None): mat = [] for i in range(m): mat.append([fill] * n) return matdef get_edges(graph): n = len(graph) edges = [] for i in range(n): for j in range(n): if graph[i][j] != 0: edges.append((i, j, graph[i][j])) return edges
make_mat
Used to initialize a two-dimensional array that get_edges
transforms a graph into a list of edges by an adjacency matrix representation.
The next step is to implement the Bellman-ford algorithm:
def ford(graph, v0): n = len(graph) edges = get_edges(graph) dis = [INF] * n dis[v0] = 0 path = [0] * n for k in range(n-1): for edge in edges: # relax if dis[edge[0]] + edge[2] < dis[edge[1]]: dis[edge[1]] = dis[edge[0]] + edge[2] path[edge[1]] = edge[0] return dis, path
It is very simple to perform iterations and slack operations after initialization.
The shortest path is obtained by Path[i], and the shortest path from vertex i to source point is obtained by successive iteration. The shortest path from source to I can be ordered in reverse order.
def show(path, start, stop): i = stop tmp = [stop] while i != start: i = path[i] tmp.append(i) return list(reversed(tmp))
The Ford algorithm allows the path to have a negative weight, but if the path has a negative total weight of the ring, each pass through the ring shortest path length will be reduced. Therefore, there is no shortest path for some points in the graph (the shortest path length is negative infinity).
If there is no negative ring in the path, there is no edge that can be relaxed after the n-1 iteration. As a result, there is a negative ring in the illustration of a slack edge.
This improves the Ford algorithm that can detect negative loops:
def ford(graph, v0): n = len(graph) edges = get_edges(graph) dis = [INF] * n dis[v0] = 0 path = [0] * n for k in range(n-1): for edge in edges: # relax if dis[edge[0]] + edge[2] < dis[edge[1]]: dis[edge[1]] = dis[edge[0]] + edge[2] path[edge[1]] = edge[0] # check negative loop flag = False for edge in edges: # try to relax if dis[edge[0]] + edge[2] < dis[edge[1]]: flag = True break if flag: return False return dis, path
Dijkstra algorithm
Dijkstra algorithm is a greedy algorithm, but it can guarantee the global optimal solution. The Dijkstra algorithm requires the same two auxiliary arrays as the Ford algorithm:
dis[i]
: Store vertex i to source point known shortest path
path[i]
: Stores vertex I to the previous vertex of the known shortest path on the source point, I.
The core of the Dijkstra algorithm is still the relaxation operation, but the method of choosing the slack edge is different. The Dijkstra algorithm uses a small top heap to store all the edges that have not been accessed, and then selects the smallest of them for relaxation at a time.
def dijkstra(graph, v0): n = len(graph) dis = [INF] * n dis[v0] = 0 path = [0] * n unvisited = get_edges(graph) heapq.heapify(unvisited) while len(unvisited): u = heapq.heappop(unvisited)[1] for v in range(len(graph[u])): w = graph[u][v] if dis[u] + w < dis[v]: dis[v] = dis[u] + w path[v] = u return dis, path
Floyd
Floyd algorithm is a multi-source shortest path algorithm using the dynamic programming idea. It also requires two auxiliary arrays, but as a multi-source shortest path algorithm, its structure is different:
dis[i][j]
: Saves the known shortest path from vertex i to Vertex J, initialized to direct connection
path[i][j]
: Saves the top and bottom vertex of the known shortest path from vertex i to Vertex J, initialized to J
def floyd(graph): # init m = len(graph) dis = make_mat(m, m, fill=0) path = make_mat(m, m, fill=0) for i in range(m): for j in range(m): dis[i][j] = graph[i][j] path[i][j] = j for k in range(m): for i in range(m): for j in range(m): # relax if dis[i][k] + dis[k][j] < dis[i][j]: dis[i][j] = dis[i][k] + dis[k][j] path[i][j] = path[i][k] return dis, path
The core of the algorithm is the Traverse vertex K, I, J. If the path from vertex i through vertex K to Vertex J is shorter than the shortest path known from I to J, the known shortest path is updated.
Three algorithms for finding the shortest path: Ford, Dijkstra and Floyd