一、算法介绍
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家迪杰斯特拉于1959年题出。是从一个节点遍历其余各节点的最短路径算法,解决的是有权图中最短路径问题。
二、算法思想
1、设G=(V,E)是一个带权有向图,把图中节点集合V分成两组,第一组为已求出最短路径的节点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径,就将该节点加入到集合S中,直到全部节点都加入到S中,算法就结束了);
2、第二组为其余未确定最短路径的节点集合(用U表示),按最短路径长度的递增次序依次把第二组的节点加入S中。在加入的过程中,总保持从源点v到S中各节点的最短路径长度不大于从源点v到U中任何节点的最短路径长度。
3、此外,每个节点对应一个距离,S中的节点的距离就是从v到此节点的最短路径长度,U中的节点的距离,是从v到此节点只包括S中的节点为中间节点的当前最短路径长度。
三、算法运作流程
3.1准备过程
此算法选用单向有权图,可以找到每个节点的临近节点表,因matlab用数字遍历更为方便,所以将A-G的字母节点改为1-7数字节点。
3.2算法初始化
1、初始时,S只包含起点s;U包含除s外的其他节点,且U中节点的距离为"起点s到该节点的距离"[例如,U中节点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
2、从U中选出"距离最短的节点k",并将节点k加入到S中;同时,从U中移除节点k。
3、更新U中各个节点到起点s的距离。之所以更新U中节点的距离,是由于上一步中确定了k是求出最短路径的节点,从而可以利用k来更新其它节点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。重复步骤(2)和(3),直到遍历完所有节点。
3.3算法过程
1、假如起点为D,终点为A,选取节点D,S={D(0)} U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}
2、根据上一轮的U选取节点C,S={D(0),C(3)}
D经过C到B:最短为DC+CB=10+3=13,小于第一步B点的无穷,所以需要更新
D经过C到E:最短为DC+CE=3+5=8,大于第一步E点的4,所以不需要更新
D经过C到F:最短为DC+CF=3+6=9,小于第一步F点的无穷,所以需要更新
U={A(∞),B(13),E(4),F(9),G(∞)}
3、根据上一轮的U选取节点E,S={D(0),C(3),E(4)}
D经过E到B:与E点不相邻,所以不需要更新
D经过E到F:最短为DE+EF=4+2=6小于第二步F的9,所以需要更新
D经过E到G:最短为DE+DG=4+8=12小于第二步G的无穷,所以需要更新
U={A(∞),B(13),F(6),G(12)}
4、根据上一轮的U选取节点F, S={D(0),C(3),E(4),F(6)}
D经过F到A:最短为DE+DF+FA=4+2+16=22,小于第三步A的无穷,所以需要更新
D经过F到B:最短为DE+EF+FB=4+2+7=13,等于第三步B的13,所以不需要更新
D经过F到G:最短为DE+EF+FG=4+2+9=15,大于第三步G的12,所以不需要更新
U={A(22),B(13),G(12)}
5、根据上一轮的U选取节点G, S={D(0),C(3),E(4),F(6),G(12)}
D经过G到A:最短为DE+EG+GA=4+8+14=26,大于第四步A的22,所以不需要更新
U={A(22),B(13)}
6、根据上一轮的U选取节点B,S={D(0),C(3),E(4),F(6),G(12),B(13)}
D经过B到A:DC+CB+BA=3+10+12=25大于第五步A的22,所以不需要更新
U={A(22)}
7、将A放入S中,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)} U={Ø}
8、即D到A的最优路径为D→E→F→A,最短距离22。
% Dijkstra算法
% 作者:Ally
% 日期:2020/12/18
clc
clear
close all
%% 图定义
% 根据节点的邻近节点表及字母节点-数字节点对应表,构造节点元胞数组
nodes_dist = cell(0);
nodes_dist(1,:) = {1, [2, 6, 7], [12, 16, 14]};
nodes_dist(2,:) = {2, [1, 3, 6], [12, 10, 7]};
nodes_dist(3,:) = {3, [2, 4, 5, 6], [10, 3, 5, 6]};
nodes_dist(4,:) = {4, [3, 5], [3, 4]};
nodes_dist(5,:) = {5, [3, 4, 6, 7], [5, 4, 2, 8]};
nodes_dist(6,:) = {6, [1, 2, 3, 5, 7], [16, 7, 6, 2, 9]};
nodes_dist(7,:) = {7, [1, 5, 6], [14, 8, 9]};
%% 算法初始化
% S/U的第一列表示节点编号
% 对于S,第二列表示从源节点到本节点已求得的最小距离,不再变更;
% 对于U,第二列表示从源节点到本节点暂时求得的最小距离,可能会变更
S = [4, 0];
U(:,1) = [1, 2, 3, 5, 6, 7];
U(:,2) = [inf, inf, 3, 4, inf, inf];
% 最优路径及暂时最优路径的初始化
path_opt = cell(7,2);
path_opt(4,:) = {4, 4};
path_temp = cell(7,2);
path_temp(3,:) = {3, [4, 3]};
path_temp(4,:) = {4, 4};
path_temp(5,:) = {5, [4, 5]};
%% 循环遍历所有节点
while ~isempty(U)
% 在U集合找出当前最小距离值及对应节点,并移除该节点至S集合中
[dist_min, idx] = min(U(:,2));
node_min = U(idx, 1);
S(end+1,:) = [node_min, dist_min];
U(idx,:) = [];
% 将最小距离值的节点添加到最优路径集合
path_opt(node_min,:) = path_temp(node_min,:);
%% 依次遍历最小距离节点的邻节点,判断是否在U集合中更新邻节点的距离值
for i = 1:length(nodes_dist{node_min, 2})
% 需要判断的节点
node_temp = nodes_dist{node_min, 2}(i);
% 找出U集合中节点node_temp的索引值
idx_temp = find(node_temp == U(:,1));
% 判断是否更新
if ~isempty(idx_temp)
if dist_min + nodes_dist{node_min, 3}(i) < U(idx_temp, 2)
U(idx_temp, 2) = dist_min + nodes_dist{node_min, 3}(i);
% 更新暂时最优路径
path_temp{node_temp, 1} = node_temp;
path_temp{node_temp, 2} = [path_opt{node_min, 2}, node_temp];
end
end
end
end
学习自B站:小黎的Ally