来自我的博客最短路径 Shortest Path - Snow’s Blog (ivansnow02.github.io)
最短路径 Shortest Path
加权有向图中每条路径都有值,其值是该路径上所有边的权值之和。最短路径 (Shortest Path)问题就是指求出两个给定顶点间权值最小的路径。
定义
两个顶点s和t之间的一条最短路径 是从s到t的一条有向简单路径,而且此路径 具有以下的性质:不存在另一条这样的路径且有更小的权值。
最短路径树(Shortest-path trees, 简称SPT)
给定一个图和一个指定的顶点s,则s的最短路径树是一个包含s以及由s可达的所有顶点的子图,它构成以s为根的一棵有向树,其中每条树路径都是图中的一条最短路径。最短路径树定义了从根到其它顶点的最短路径。
分类
图中最短路径问题主要是以下三类:
- 源点-汇点最短路径
- 单源最短路径
- 全源最短路径
算法
求图中最短路径的算法常用的有:
- Dijkstra算法
- Floyd-Warshall算法
- Bellman-Ford算法
### 基本操作
边松弛(edge relaxation)
检查一条给定的边,是否可以通过该边,对到其所指顶点的最短路径进行更新。
不妨设有条边e=(u,v)
,它的长度是e.dist
, d[i]
表示源点到顶点i的最短距离,则松弛操作如下: if (d[v] > d[u] + e.dist) d[v] = d[u] + e.dist
上式的意思就是:如果从源点到u的最短距离加上u到v的长度小于当前源点到v的最短距离,那么更新源点到v的最短距离。 边松弛体现在Dijkstra算法和Bellman-Ford算法中
路径松弛(path relaxation)
检查一个给定顶点,是否可以使得连接另外两个给定顶点的最短路径进行更新。
不妨设现在有一个顶点x,还有另外两个顶点s和t。考虑能否通过x,使得s到t的最短距离变的更小,能的话,就进行路径松弛:if (d[s][t] > d[s][x] + d[x][t]) d[s][t] = d[s][x] + d[x][t]
上式的意思是:如果s到x的最短距离加上x到t的最短距离小于s到t的最短距离,那么更新s到t的最短距离。路径松弛体现在Floyd-Warshall算法中。
Dijkstra算法
Dijkstra算法计算单源最短路径,它的基本思想:开始把源点放在SPT中,然后,每次增加一条边来构造SPT,所取的边总是可以给出从源点到尚未在SPT中的一个顶点的最短路径。也就是说,按照顶点与起始顶点的距离(通过SPT)为顺序来加入顶点。即:每次选不在SPT中距离源点最近的点加
入SPT。
具体步骤
- 初始化:顶点集S只包含源点,即S={v},顶点集U包含除v外的其他顶点。
- 从U中选取一个顶点u,它是源点v到U中最短路径长度最小的顶点,然后把顶点u加入S中(此时求出了源点v到顶点u的最短路径长度)。
- 以顶点u为新考虑的中间点,修改顶点u的出边邻接点j的最短路径长度,此时源点v到顶点j的最短路径有两条,即一条经过顶点u,一条不经过顶点u。
- 重复步骤2和3,直到S包含所有的顶点即U为空。
基于堆的Dijkstra算法代码
struct Edge {
int from, to, dist;
Edge(int u, int v, int d) : from(u), to(v), dist(d) {
}
};
struct HeapNode {
int d, u; // d表示该点到源点的距离,u是该点的编号
bool operator< (const HeapNode& rhs) const {
return d > rhs.d;
} // 小根堆
};
vector<Edge> edges; // edges存所有的边的信息
vector<int> G[MAXN]; // G[i]是顶点i发出的所有边
bool done[MAXN]; // 是否已经加入SPT
int d[MAXN]; // 每个点到源点的最短路径
int p[MAXN]; // 记录到顶点i的是哪条边
void Dijkstra(int s, int V) {
// 计算所有顶点到顶点s的最短路径
priority_queue<HeapNode> Q;
for(int i = 0; i < V; ++i) d