迪克斯特拉(Dijkstra)算法

  • 赋权图最短路问题 - 图1,求赋权图中指定的两个顶点最短路问题 - 图2间的具有最小权的路,这条路称为最短路问题 - 图3间的最短路,它的权称为最短路问题 - 图4间的距离,记为最短路问题 - 图5
  • 迪克斯特拉(Dijkstra)算法基本思想是每一次都走最短的路,可以处理有向图或无向图。
  • 当权为负数时,不能使用Dijkstra算法,要使用Floyd算法。

    例子

    某公司在6个城市最短路问题 - 图6有分公司,下表是两个城市之间的票价,若无直接航班,则为最短路问题 - 图7。求最短路问题 - 图8到其他城市的票价最便宜的路线图。
    最短路问题 - 图9

    演示过程

  • [x] 先理解迪克斯特拉(Dojkstra)算法的思想,故演示如何寻找最短路径

graph.png

  1. 初始化,visited是是否被标记为最优路径,distance是和初始节点的距离,parent_index是当前节点的父节点 | 节点(index) | 1 | 2 | 3 | 4 | 5 | 6 | | —- | —- | —- | —- | —- | —- | —- | | visited | 0 | 0 | 0 | 0 | 0 | 0 | | distance | inf | inf | inf | inf | inf | inf | | parent_index | -1 | -1 | -1 | -1 | -1 | -1 |

  2. 从节点1开始,当前节点为1 | 节点(index) | 1 | 2 | 3 | 4 | 5 | 6 | | —- | —- | —- | —- | —- | —- | —- | | visited | 1 | 0 | 0 | 0 | 0 | 0 | | distance | 0 | inf | inf | inf | inf | inf | | parent_index | 1 | -1 | -1 | -1 | -1 | -1 |

  3. 将(未被访问节点的distance)和(当前节点的distance+当前节点到未被访问节点的距离)比较大小,由于40<inf,25<inf,10<inf,所以更新distance和parent_index | 节点(index) | 1 | 2 | 3 | 4 | 5 | 6 | | —- | —- | —- | —- | —- | —- | —- | | visited | 1 | 0 | 0 | 0 | 0 | 0 | | distance | 0 | inf | inf | 40 | 25 | 10 | | parent_index | 1 | -1 | -1 | 1 | 1 | 1 |

未被访问节点中节点6的distance最小,所以标记6为已访问,当前节点为6

节点(index) 1 2 3 4 5 6
visited 1 0 0 0 0 1
distance 0 inf inf 40 25 10
parent_index 1 -1 -1 1 1 1
  1. 10+a(6,2)=10+25=35<inf,更新节点2的distance和parent_index

10+a(6,3)=10+inf,不更新节点3
10+a(6,4)=10+25=35<40,更新节点4的distance和parent_index
10+a(6,5)=10+55>25,不更新节点5

节点(index) 1 2 3 4 5 6
visited 1 0 0 0 1 1
distance 0 35 inf 35 25 10
parent_index 1 6 -1 6 1 1

未被访问节点中节点5的distance最小,所以标记5为已访问,当前节点为5

节点(index) 1 2 3 4 5 6
visited 1 0 0 0 1 1
distance 0 35 inf 35 25 10
parent_index 1 6 -1 6 1 1
  1. 25+a(5,2)=25+inf,不更新节点2

25+a(5,3)=25+20=4525+a(5,4)=25+10=35,不更新节点4

节点(index) 1 2 3 4 5 6
visited 1 0 0 0 1 1
distance 0 35 45 35 25 10
parent_index 1 6 5 6 1 1

未被访问节点中节点2的distance最小,所以标记2为已访问,当前节点为2(2和4一样大,默认序号小的那个)

节点(index) 1 2 3 4 5 6
visited 1 1 0 0 1 1
distance 0 35 45 35 25 10
parent_index 1 5 5 6 1 1

同样的方法得到最终结果:

节点(index) 1 2 3 4 5 6
visited 1 1 1 1 1 1
distance 0 35 45 35 25 10
parent_index 1 5 5 6 1 1

matlab实现Dijkstra算法

  1. clc,clear
  2. %% 初始化邻接矩阵
  3. a=zeros(6);%邻接矩阵初始化
  4. a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
  5. a(2,3)=15;a(2,4)=20;a(2,6)=25;
  6. a(3,4)=10;a(3,5)=20;
  7. a(4,5)=10;a(4,6)=25;
  8. a(5,6)=55;
  9. a=a+a';
  10. a(a==0)=inf;
  11. for i=1:length(a)
  12. a(i,i)=0;
  13. end
  14. %% 确定初始节点,初始化变量
  15. start_index=1;%初始节点
  16. temp=start_index;%当前节点
  17. distance=repelem(inf,length(a));%初始化所有节点和初始节点的距离
  18. distance(temp)=0;
  19. parent_index=repelem(-1,length(a));%父节点
  20. parent_index(temp)=1;
  21. visited=zeros(1,length(a));%节点是否已经被访问
  22. visited(temp)=1;%节点1被访问
  23. %%各节点到初始节点的最短距离
  24. while sum(visited)<length(a)%节点没有被访问完
  25. unvisited=find(visited==0);%将还没有被访问的节点索引放进unvisited
  26. for i=unvisited
  27. if distance(temp)+a(temp,i)<distance(i)
  28. distance(i)=distance(temp)+a(temp,i);
  29. parent_index(i)=temp;
  30. end
  31. end
  32. [min_distance,index]=min(distance(unvisited));%找到未被访问的节点和当前节点的最近距离和节点
  33. temp=unvisited(index);%最小距离对应的下一个节点是unvisited中第index个值
  34. visited(temp)=1;
  35. end
  36. disp(["各节点为" num2str([1:1:length(a)])]);
  37. disp(["各节点的父节点为" num2str(parent_index)]);
  38. disp(["各节点和初始节点的最短距离" num2str(distance)]);
  39. %%各节点到初始节点的最短路径
  40. for i=1:length(a)
  41. end_index=i;
  42. path=[end_index];
  43. while end_index ~= start_index && parent_index(end_index)~=-1
  44. path=[path,parent_index(end_index)];
  45. end_index=parent_index(end_index);
  46. end
  47. if parent_index(end_index)==-1
  48. disp([num2str(i) "无法到达初始节点"]);
  49. else
  50. disp([num2str(i) "到初始节点的最短路径" num2str(fliplr(path))]);%fliplr反转
  51. end
  52. end
  53. "各节点为" "1 2 3 4 5 6"
  54. "各节点的父节点为" "1 6 5 6 1 1"
  55. "各节点和初始节点的最短距离" "0 35 45 35 25 10"
  56. "1" "到初始节点的最短路径" "1"
  57. "2" "到初始节点的最短路径" "1 6 2"
  58. "3" "到初始节点的最短路径" "1 5 3"
  59. "4" "到初始节点的最短路径" "1 6 4"
  60. "5" "到初始节点的最短路径" "1 5"
  61. "6" "到初始节点的最短路径" "1 6"

python实现Dijkstra算法

  1. a=np.array([[0,50,np.inf,40,25,10],[0,0,15,20,np.inf,25],[0,0,0,10,20,np.inf],[0,0,0,0,10,25],[0,0,0,0,0,55],[0,0,0,0,0,0]]);
  2. a=a+a.T
  3. a
  4. ## 确定初始节点,初始化变量
  5. start_index=0#初始节点
  6. temp=start_index#当前节点
  7. distance=np.array([float('inf')]*len(a))#初始化所有节点和初始节点的距离
  8. distance[temp]=0
  9. parent_index=np.array([-1]*len(a))#父节点
  10. parent_index[temp]=0
  11. visited=np.zeros(len(a))#节点是否已经被访问
  12. visited[temp]=1#节点1被访问
  13. ## 各节点到初始节点的最短距离
  14. while np.sum(visited)<len(a):#节点没有被访问完
  15. unvisited=np.where(visited==0)[0]#将还没有被访问的节点索引放进unvisited
  16. for i in unvisited:
  17. if distance[temp]+a[temp,i]<distance[i]:
  18. distance[i]=distance[temp]+a[temp,i]
  19. parent_index[i]=temp
  20. min_distance=min(distance[unvisited])
  21. index=np.where(distance[unvisited]==min_distance)[0][0] #第一个0是因为where得到的是一个元组,要把数组从元组里拿出来,
  22. #第二个0是当有多个索引时选择第一个
  23. temp=unvisited[index]#最小距离对应的下一个节点是unvisited中第index个值
  24. visited[temp]=1
  25. ## 初始节点到某个节点的最短路径
  26. parent_index=list(parent_index)#转换成列表可以使用append方法,比较方便
  27. end_index=2
  28. path=[end_index]
  29. while end_index != start_index and parent_index[end_index]!=-1:
  30. path.append(parent_index[end_index])
  31. end_index=parent_index[end_index]
  32. if parent_index[end_index]==-1:
  33. print("初始节点%d无法到达节点%d"%(start_index,end_index))
  34. else:
  35. print("初始节点%d无法到达节点%d的最短路径为:"%(start_index,end_index))
  36. print(path[::-1])#path倒序输出

matlab自带最短路径函数

  1. %% matlab自带算法
  2. s = [1 1 1 1 2 2 2 3 3 4 4 5];
  3. t = [2 4 5 6 3 4 6 4 5 5 6 6];
  4. w = [50 40 25 10 15 20 25 10 20 10 25 55];
  5. G = graph(s,t,w);%无向图,digraph有向图
  6. plot(G, 'EdgeLabel', G.Edges.Weight, 'linewidth', 2) %画出图
  7. set( gca, 'XTick', [], 'YTick', [] );
  8. [P,d] = shortestpath(G, 1, 4); %P14的最短路径,d是最短距离,
  9. %无向图且所有边权重均为非负数时默认Dijkstra算法,具体情形可自行查询
  10. % 在图中高亮我们的最短路径
  11. myplot = plot(G, 'EdgeLabel', G.Edges.Weight, 'linewidth', 2); %首先将图赋给一个变量
  12. highlight(myplot, P, 'EdgeColor', 'r') %对这个变量即我们刚刚绘制的图形进行高亮处理(给边加上r红色)
  13. % 求出任意两点的最短路径矩阵
  14. D = distances(G);
  15. % 找出给定范围内的所有点 nearest(G,s,d)
  16. % 返回图形 G 中与节点 s 的距离在 d 之内的所有节点
  17. [nodeIDs,dist] = nearest(G, 2, 30);
  18. >> P
  19. P =
  20. 1 5 4
  21. >> d
  22. d =
  23. 35
  24. >> D
  25. D =
  26. 0 35 45 35 25 10
  27. 35 0 15 20 30 25
  28. 45 15 0 10 20 35
  29. 35 20 10 0 10 25
  30. 25 30 20 10 0 35
  31. 10 25 35 25 35 0
  32. >> nodeIDs
  33. nodeIDs =
  34. 3
  35. 4
  36. 6
  37. 5
  38. >> dist
  39. dist =
  40. 15
  41. 20
  42. 25
  43. 30

image.png

Floyd算法

  • Floyd算法可以得到任意两个顶点之间的最短距离和路径
  • 对于赋权图最短路问题 - 图12,其中顶点集最短路问题 - 图13,邻接矩阵

最短路问题 - 图14
最短路问题 - 图15
最短路问题 - 图16

  • Floyd算法的基本思想是递推产生一个矩阵序列最短路问题 - 图17最短路问题 - 图18表示从顶点最短路问题 - 图19到顶点最短路问题 - 图20的路径上所经过的顶点序号不大于k的最短路径长度。
  • 计算时用迭代公式

最短路问题 - 图21
最后,当最短路问题 - 图22时,最短路问题 - 图23即是各顶点之间的最短通路值。

演示过程

最短路问题 - 图24
最短路问题 - 图25最短路问题 - 图26
最短路问题 - 图27
最短路问题 - 图28最短路问题 - 图29
最短路问题 - 图30
同理,得
最短路问题 - 图31最短路问题 - 图32
顶点 2 到顶点 5 得最短距离是最短路问题 - 图33最短路问题 - 图34,2——4——5
看2,4中间是否还有点,由于最短路问题 - 图35,所以没有其他点
看4,5中间是否还有点,由于最短路问题 - 图36,所以没有其他点
所以顶点 2 到顶点 5 得最短路径为:2——4——5

matlab代码

  1. clc,clear
  2. %% 初始化邻接矩阵
  3. a=zeros(6);%邻接矩阵初始化
  4. a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
  5. a(2,3)=15;a(2,4)=20;a(2,6)=25;
  6. a(3,4)=10;a(3,5)=20;
  7. a(4,5)=10;a(4,6)=25;
  8. a(5,6)=55;
  9. a=a+a';
  10. a(a==0)=inf;
  11. for i=1:length(a)
  12. a(i,i)=0;
  13. end
  14. %% 初始化path矩阵
  15. n = size(a,1);
  16. dist = a;
  17. path = repelem(-1,n,n);
  18. %% 下面开始三个循环
  19. for k=1:n % 中间节点k从1- n 循环
  20. for i=1:n % 起始节点i从1- n 循环
  21. for j=1:n % 终点节点j从1-n 循环
  22. if dist(i,j)>dist(i,k)+dist(k,j) % 如果i,j两个节点间的最短距离大于i和k的最短距离+k和j的最短距离
  23. dist(i,j)=dist(i,k)+dist(k,j); % 那么我们就令这两个较短的距离之和取代i,j两点之间的最短距离
  24. path(i,j)=k; % 起点为i,终点为j的两个节点之间的最短路径要经过的节点更新为path(i,k)
  25. end
  26. end
  27. end
  28. end
  29. %% 找出任意两个顶点间的最短路劲
  30. sb=1;%起点
  31. db=6;%终点
  32. d=dist(sb,db);
  33. parent=path(sb,:);
  34. parent(parent==-1)=sb;
  35. mypath=db;t=db;
  36. while t~=sb
  37. p=parent(t);
  38. mypath=[p,mypath];
  39. t=p;
  40. end

该算法由matlab代码写出python代码就很简单了,不再展示