路的长度:赋权图的路经过的边的权和
最短路:从顶点 v i v_i vi到顶点 v j v_j vj的路中,长度最小的一条路。
顶点间的距离:顶点 v i v_i vi到顶点 v j v_j vj的最短路的长度。记作 d ( u 0 , v 0 ) d(u_0,v_0) d(u0,v0)
已知赋权无向图 G ( V , E , W ) G(V,E,W) G(V,E,W)
固定顶点 u o u_o uo ,求 u 0 u_0 u0到其余顶点 v v v的最短路及距离 d ( u 0 , v ) d(u_0,v) d(u0,v)
算法思想:最短路径的子路也是最短路。固定 u 0 u_0 u0,从近到远求出 u 0 u_0 u0到其余顶点之一 v v v的最短路和距离,直至遍历完所有顶点(或到某一特定顶点 v 0 v_0 v0 )。
记号说明:
| 记号 | 含义 |
|---|---|
| d n o w ( v ) d_{now}(v) dnow(v) | 从顶点 u 0 u_0 u0当前顶点 v v v的路径长度 |
| p a r e n t s ( v ) parents(v) parents(v) | 最短路径中当前顶点 v v v的上一个顶点 |
| S i S_i Si | 含i个顶点的最短路径顶点集 |
算法结束后, S i S_i Si为最短路径集。 d n o w ( v ) d_{now}(v) dnow(v)为距离集。
算法局限:要求权值为正数
% Dijkstra.m
function [path, distance] = Dijkstra(G, u0, v0)
% Dijkstra算法求最短路径
% 初始化参数
n = G.numnodes;
nodes = 1:n;
i = 0;
d_now = ones(n, 1) * inf;
d_now(u0) = 0;
parents = ones(n, 1) * u0;
in_s = u0;
history = [];
% 遍历所有终点或v0进入最短路径集
while (i ~= n - 1) && ~(ismember(v0, in_s))
not_in_s = setdiff(nodes, in_s);
for v = not_in_s
for u = in_s
% 查找uv间的边
uv_index = G.findedge(u, v);
if uv_index
% 边存在则获取权
w = min(G.Edges.Weight(uv_index));
% 计算新的最短距离
d_now(v) = min([d_now(v), d_now(u) + w]);
if d_now(u) + w <= d_now(v)
% 距离发生改变
parents(v) = u;
% 记录变化
history = [history; [u, v]];
end
end
end
end
% 寻找最短距离的顶点
tmp = d_now;
tmp(in_s) = inf;
[~, new_u] = min(tmp);
new_u = new_u(1);
% 添加最短路径顶点
in_s = union(new_u, in_s);
i = i + 1;
end
% 倒溯最短路径
last = v0;
path = v0;
while last ~= u0
k = find(history(:, 2) == last);
last = history(k(end), 1);
path = [last, path];
end
distance = d_now(v0);
end
示例:
clc,clear
% 输入图的顶点对
E=[ 1 2 2;1 3 8;1 4 1;
2 3 6;2 5 1;3 4 7;
3 5 5;3 6 1;3 7 2;
4 7 9;5 6 3;5 8 2;
5 9 9;6 7 4;6 9 6;
7 9 3;7 10 1;8 9 7;
8 11 9;9 10 1;9 11 2;
10 11 4];
% 创建并绘制图
G=graph(E(:,1),E(:,2),E(:,3));
figure=plot(G,"EdgeLabel",G.Edges.Weight,'EdgeLabelColor','green');
% 使用Dijkstra算法求得最短路径并在图中标出
[path,distance]=Dijkstra(G,1,11);
highlight(figure,path,'EdgeColor','red','LineWidth',2,"EdgeLabelColor","red");

def Dijkstra(G, u0, v0):
"""利用Dijkstra计算最短路径和距离"""
# 初始化参数
n = G.number_of_nodes()
nodes = list(range(1, n + 1))
d_now = np.ones(n) * np.inf
d_now[u0 - 1] = 0
parents = [u0 for i in range(n)]
s = set()
s.add(u0)
history = []
i = 0
# 遍历所有终点或v0进入最短路径顶点集
while (i != n - 1) or (v0 not in s):
not_s = set(nodes) - s
for v in not_s:
for u in s:
try:
# 检查顶点u,v间是否有边
w = G.edges[u, v]['weight']
except:
pass
else:
d_now[v - 1] = min([d_now[v - 1], d_now[u - 1] + w])
if d_now[u - 1] + w <= d_now[v - 1]:
# 顶点最短值改变,记录改变
parents[v - 1] = u
history.append([u, v])
# 寻找当前的最小距离点,并加入s集合
tmp = d_now.copy()
in_s_index = [i - 1 for i in s]
tmp[in_s_index] = np.inf
new_u = np.argmin(tmp) + 1
s.add(new_u)
i = i + 1
# 拼接最终的最短路径
last = v0
path = [v0]
history = np.array(history)
while last != u0:
k = np.where(history[:, 1] == last)
last = history[k[-1][-1], 0]
path.append(last)
path = path[-1::-1]
distance = d_now[v0 - 1]
return path, distance
示例:
# 导库
import networkx as nx
import numpy as np
# 输入边和权
edges = [(1, 2, 2), (1, 3, 8), (1, 4, 1), (2, 3, 6), (2, 5, 1), (3, 4, 7),
(3, 5, 5), (3, 6, 1), (3, 7, 2), (4, 7, 9), (5, 6, 3), (5, 8, 2),
(5, 9, 9), (6, 7, 4), (6, 9, 6), (7, 9, 3), (7, 10, 1), (8, 9, 7),
(8, 11, 9), (9, 10, 1), (9, 11, 2), (10, 11, 4)]
# 创建空图并添加顶点和边权
G = nx.Graph()
G.add_weighted_edges_from(edges)
# 计算顶点位置
pos = nx.spring_layout(G)
# 绘制无权图
nx.draw(G, pos, with_labels=True, font_size=14)
# 绘制最短路径
path,distance=Dijkstra(G,1,11)
shortest_edges=[]
for i in range(np.size(path)-1):
shortest_edges.append((path[i],path[i+1]))
nx.draw_networkx_edges(G,pos,edgelist=shortest_edges,edge_color='red')
# 追加绘制权
labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos,edge_labels=labels,font_color="green")

已知赋权无向图 G ( V , E , W ) G(V,E,W) G(V,E,W)
求所有顶点对 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)之间的最短距离及对应最短路径
算法思想:依次将每个顶点作为可能的中间点,插入所有的顶点对之间进行判断,若最短距离更新,则将该点记录下来。直到n次之后,得到所有顶点对的最短距离,并通过之间的记录反推最短路径。
记号说明:
| 记号 | 含义 |
|---|---|
| d i s t a n c e s distances distances | 距离变换矩阵。算法结束后则为顶点之间的最短距离矩阵 |
| R R R | 路由矩阵。记录顶点间的中间点变化 |
算法局限:所有圈的权值和非负
% Floyd.m
function [distances, paths] = Floyd(G)
% Floyd算法求所有顶点对之间的最短路径
% 矩阵初始化
distances = G.adjacency('weighted');
n = length(distances);
distances(distances == 0) = inf;
distances(1:n + 1:end) = 0;
R = zeros(n);
for k = 1:n
for i = 1:n
for j = 1:n
% 插点更新
if distances(i, k) + distances(k, j) < distances(i, j)
distances(i, j) = distances(i, k) + distances(k, j);
R(i, j) = k;
end
end
end
end
distances = full(distances);
% 复原路径
paths = returnPaths(R);
for i = 1:n
for j = 1:n
if i ~= j
% 添加起点和终点
paths{i, j} = [i paths{i, j} j];
else
paths{i, j} = i;
end
end
end
end
% returnPaths.m
function paths = returnPaths(R)
% 根据路由矩阵返回最短路径矩阵
n = length(R);
paths = cell(n);
for i = 1:n
for j = 1:n
paths{i, j} = parse(i, j);
end
end
% 递归推导最短路径
function path = parse(i, j)
p = R(i, j);
if p == 0
path = [];
else
left = parse(i, p);
right = parse(p, j);
path = [left, p, right];
end
end
end
示例:
clear,clc
E=[ 1 2 2;1 3 8;1 4 1;
2 3 6;2 5 1;3 4 7;
3 5 5;3 6 1;3 7 2;
4 7 9;5 6 3;5 8 2;
5 9 9;6 7 4;6 9 6;
7 9 3;7 10 1;8 9 7;
8 11 9;9 10 1;9 11 2;
10 11 4];
G=graph(E(:,1),E(:,2),E(:,3));
n=G.numnodes;
[distances,paths]=Floyd(G)


def returnPaths(R):
"""根据路由矩阵还原路径"""
def parse(i, j):
"""递归解析i,j顶点最短路径"""
p = R[i, j]
if p == 0:
path = []
else:
path = [p]
left = parse(i, p - 1)
right = parse(p - 1, j)
path.extend(right)
for num in left[-1::-1]:
path.insert(0, num)
return path
n, n = np.shape(R)
paths = [[] for i in range(n)]
for i in range(n):
for j in range(n):
paths[i].append(parse(i, j))
return paths
def Floyd(G):
"""Floyd算法求所有顶点间的最短路径"""
# 初始化矩阵
distances = nx.to_numpy_matrix(G)
n, n = np.shape(distances)
distances[distances == 0] = np.inf
row, col = np.diag_indices_from(distances)
distances[row, col] = 0
R = np.zeros((n, n), dtype=int)
# 插点更新
for k in range(n):
for i in range(n):
for j in range(n):
if distances[i, k] + distances[k, j] < distances[i, j]:
distances[i, j] = distances[i, k] + distances[k, j]
R[i, j] = k + 1
# 复原路径
paths = returnPaths(R)
# 路径添加起点和终点
for i in range(n):
for j in range(n):
if i != j:
paths[i][j].append(j + 1)
paths[i][j].insert(0, i + 1)
else:
paths[i][j] = [i + 1]
return distances, paths
示例:
# 导库
import networkx as nx
import numpy as np
import pandas as pd
# 输入边和权
edges = [(1, 2, 2), (1, 3, 8), (1, 4, 1), (2, 3, 6), (2, 5, 1), (3, 4, 7),
(3, 5, 5), (3, 6, 1), (3, 7, 2), (4, 7, 9), (5, 6, 3), (5, 8, 2),
(5, 9, 9), (6, 7, 4), (6, 9, 6), (7, 9, 3), (7, 10, 1), (8, 9, 7),
(8, 11, 9), (9, 10, 1), (9, 11, 2), (10, 11, 4)]
# 创建空图并添加顶点和边权
G = nx.Graph()
G.add_weighted_edges_from(edges)
# Floyd算法
distances, paths = Floyd(G)
n = G.number_of_nodes()
d_df = pd.DataFrame(distances, index=range(1, n + 1), columns=range(1, n + 1))
d_df

p_df = pd.DataFrame(paths, index=range(1, n + 1), columns=range(1, n + 1))
p_df

[path,distance] = shortestpath(G,u0,v0) # 求u0到v0的最短路径和最短距离
D=distances(G) # 求顶点对间的最短距离矩阵
import networkx as nx
import pandas as pd
path = nx.dijkstra_path(G, u0, v0) # Dijkstra算法求两点的最短路径列表
distance = nx.dijkstra_path_length(G, u0, v0) # Dijkstra算法求两点的最短距离
path = nx.shortest_path(G, u0, v0,weight='weight') # 求两点的最短路径列表
distance = nx.shortest_path_length(G, u0, v0,weight='weight') # 求两点的最短距离
distances = dict(nx.all_pairs_dijkstra_path_length(G)) # 求顶点对之间的最短距离字典
d_df = pd.DataFrame(distances).T
d_df
paths = dict(nx.all_pairs_dijkstra_path(G)) # 求顶点对之间的最短路径字典
p_df = pd.DataFrame(paths).T
p_df