最短路径问题
前言
解决最短路径问题首先了解如下概念:
图的基本概念
1.无向图概念
2.有向图概念
3. 完全图、二分图
完全图:指图的两顶点间有边相连,两顶点相邻,顶点都相邻的图称为完全图。
简单图:任两点间最多有一条边,且每条边的两个端点都不重合的图称为简单图。
4.子图、顶点的度
图 H 叫做图 G 的子图(subgraph),记作 H ⊂ G ,如果 V (H ) ⊂V (G) , E(H) ⊂ E(G) 。若 H 是G 的子图,则G 称为 H 的母图。 G 的支撑子图(spanning subgraph,生成子图)是指满足V(H) =V(G) 的子 图 H 。
顶点的度:设v ∈V (G) ,G 中与v 关联的边数(每个环算作两条边)称为v 的度(degree),记 作d(v)。若d(v)是奇数,称v 是奇顶点(odd point);d(v)是偶数,称v 是偶顶点(even point)。
(i)
(ii) 任意一个图的奇顶点的个数是偶数。
图与网络的数据结构
首先假设 G = (V, A)是一个简单有向图,顶点集合V={v1,v2,…,vn},边集E={e1,…,en},记|V |= n,| E|= m 。
1.邻接矩阵
采用邻接矩阵表示图,直观方便,通过查看矩阵中的元素就可以判断任意两点之间是否有边,以及边上的值。
- 例题
图 2 所示的有向图,可以用邻接矩阵表示为
2.稀疏矩阵
- 对于有向图的邻接矩阵转化为稀疏矩阵
S = sparse(A)
将矩阵A转化为稀疏矩阵形式,即矩阵A中任何0元素被去除,⾮零元素及其下标组成矩阵S。如果A本⾝是稀疏的,sparse(S)返回S。 - 对于无向图地的邻接矩阵转化为稀疏矩阵
由于邻接矩阵是对矩阵,Matlab中只需要使用邻接矩阵的下三角元素,即Matlab只储存邻接矩阵下三角元素中的非零元素。 - 稀疏矩阵转化为普通矩阵
A=full(S)
把稀疏矩阵S转换为全矩阵存储形式A - Matlab工具的使用
一、Dijkstra算法解决两个指定顶点之间的最短路径问题。
问题如下:给出了一个连接若干个城镇的铁路网络,在这个网络的两个指定城镇间, 找一条最短铁路线。
Dijkstra算法 :
- 例题1
某公司在六个城市c(1),c(2),…,c(6) 中有分公司,从 c(i))到 c(j) 的直接航程票价记在如下矩阵的 ( i,j ) 位置上(∞表示无直接航路)。请帮助该公司设计一张城市 c(1)到其它城市间的票价最便宜的路线图。
用矩阵 a(nxn) (n为顶点个数)存放各边权的邻接矩阵,行向量pb, index(1), index(2) , d 分别用来存放P 标号信息、标号顶点顺序、标号顶点索引、短通路的值。其中分量为
第一个城市到其他城市的最短路径。
clc,clear
a=zeros(6); % 邻接矩阵初始化
a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
a(2,3)=15;a(2,4)=20;a(2,6)=25;
a(3,4)=10;a(3,5)=20;
a(4,5)=10;a(4,6)=25;
a(5,6)=55;
a=a+a';
a(find(a==0))=inf; % 将零元素替换成无穷
pb(1:length(a))=0;pb(1)=1;index1=1;index2=ones(1,length(a));
d(1:length(a))=inf;d(1)=0; % 最短路径的值初始化
temp=1; % 最新的p标号的顶点
while sum(pb)<length(a)
tb=find(pb==0);
d(tb)=min(d(tb),d(temp)+a(temp,tb));
tmpb=find(d(tb)==min(d(tb)));
temp=tb(tmpb(1)); % 可能有多个点同时到达最小值,只取其中一个
pb(temp)=1;
index1=[index1,temp];
temp2=find(d(index1)==d(temp)-a(temp,index1));
index2(temp)=index1(temp2(1)); % 存放初始到i顶点中,第i顶点前一顶点
end
d, index1, index2
输出为
d =
0 35 45 35 25 10
index1 =
1 6 5 2 4 3
index2 =
1 6 5 6 1 1
二、两个指定顶点之间的最短路径问题的数学规划模型
三、Floyd算法解决每对顶点之间的最短路径
Floyd算法为:
- 用Floyd算法解决上述例题1
1.矩阵path用来存放每对顶点之间最短路径上所经过的顶点的序号。
clear;clc;
n=6; a=zeros(n);
a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
a(2,3)=15;a(2,4)=20;a(2,6)=25;
a(3,4)=10;a(3,5)=20;
a(4,5)=10;a(4,6)=25;
a(5,6)=55;
a=a+a';
a(a==0) = inf; % 把所有零元素替换成无穷
a([1:n+1:n^2]) = 0; % 对角元素替换成零,Matlab中数据是逐列来存储
path=zeros(n); % 初始panth为零矩阵
for k=1:n % 中间节点k从1- n 循环
for i=1:n % 起始节点i从1- n 循环
for j=1:n % 终点节点j从1-n 循环
if a(i,j)>a(i,k)+a(k,j) % 如果i,j两个节点间的最短距离大于i和k的最短距离+k和j的最短距离
a(i,j)=a(i,k)+a(k,j);
path(i,j)=k; % 起点为i,终点为j的两个节点之间的最短路径要经过的节点更新为path(i,k)
end
end
end
end
path
输出为
path =
0 6 5 5 0 0
6 0 0 0 4 0
5 0 0 0 0 4
5 0 0 0 0 0
0 4 0 0 0 1
0 0 4 0 1 0
2.起点sb到终点db通用的Floyd算法
clc,clear
function [dist,mypath] = myfloyd(a,sb,db)
% dist表示最短路的距离;mypath表示最短的路径
% a表示邻接矩阵;a(i,j)表示顶点i到j之间的直达距离,可以是有向的
% sb是起点标号;db是终点的标号
n = size(a,1);
path = zeros(n);
for k=1:n
for i=1:n
for j=1:n
if a(i,j)>a(i,k)+a(k,j) % 开始迭代
a(i,j)=a(i,k)+a(k,j);
path(i,j)=k;
end
end
end
end
dist = a(sb,db);
parent = path(sb,:); % 从起点到终点的最短路上各顶点的前驱顶点
parent(parent==0) = sb; % path中的分量为0,表示该点的前驱是起点
mypath = db;t = db;
while t ~= sb
p = parent(t);mypath = [p,mypath];
t = p;
end
end
总结
- Dijkstra算法和Floyd算法详细使用
- 它们区别在:
1.Dijkstra不能处理负权图,Flyod能处理负权图。
2.Dijkstra处理单源最短路径而Flyod是处理多源最短路径。
3.Dijkstra时间复杂度为O(n^2), Flyod时间复杂度为O(n^3) 空间复杂度为O(n ^ 2);
如果题目中是单源点正权图,就用Dijkstra算法,如果是任意两个点之间的最短路径或者是负权图,就用Floyd。