最短路 –单源最短路
1
ps: 单源最短路径代码上和应用上都非常的简单 但是跟人感觉推证它的正确性有些复杂 苦思冥想了好几天 有那么一点头绪 做一下记录
dijkstra的单源最短路径 被认为是求解无负权最短路径问题的最好放法 其中包含了 两个思想 一个是贪心 一个是最优子结构
推理
先解释几个后面需要用到的几个符号w(i,j)表示从i到j的权值 m(i,j)表示i到j的最短路径的权值总和 p(i,v1,v2,v3…,j) 表示i到j的一条路径权值总和
一个定理:如果存在m(i,j)=p(i,v1,v2,v3,j); 则m(i,j)=m(i,v3)+w(v3,j);
用反证法来证明这个定理的正确性 假设如果存在m(i,j)=p(i,v1,v2,v3,j); m(i,j)!=m(i,v3)+w(v3,j)
那么 m(i,j)=p(i,k1,k2…,v3)+w(v3,j) 而p(i,k1,k2…v3)>m(i,v3) 显然与m(i,j)是最短距离矛盾 所以
即可得定理;
此算法的特点是 每次都能选出 一个离 源点最短的点 一共需要n 次即可得到 源点到所有点的最短距离
注意这个n次 是我们初始的时候没有更新 源点到各点的距离 而是全部设为无穷(至少为比权值中最大值还要大1) 如果是 初始化就更新了 源点到每一个点的权值 那么就只需要 n-1次循环得到
再换一种思考方式 如果 有两个集合 一个是最短路径的点集m 一个是非最短路径点集n 一个源点s 起始状态为m只有s 第一轮松弛 得出源点到其他点的距离 然后从n集合中 找到了 一个离a最短的点 然后松弛n
不断循环 下去 每次 m中会多一个节点 n中会少一个 一直到n为空结束 一共执行了 n次 这其中正是蕴含了 我们前面证明的那个定理
**Dijkstra**
时间复杂度 O(n*n) n为节点数量
###核心代码:
```
for(int i=0;i<n;i++)
{
int x,m=inf;
*寻找从源点出发的一条最短路径 记录终点*
for(int y=0;y<n;y++)
if(!vis[y]&&d[y]<=m)
m=d[x=y];
*把即将松弛的中间点标记*
vis[x]=1;
*根据中间点来调整源点到每一个点的距离 如果经过源点到中间点再到终点的距离小于从从源点到终点*
*则进行更新距离*
for(int y=0;y<n;y++)
d[y]=min(d[y],d[x]+map[x][y])
}
```
2 邻接表表示图
邻接表的构造方式为 firtst 和next 下都是存储的边 下图为图中的边
起点 | 终点 | 权值 | 边号 |
---|---|---|---|
1 | 4 | 9 | 1号边 |
4 | 3 | 8 | 2号边 |
1 | 2 | 5 | 3号边 |
2 | 4 | 6 | 4号边 |
1 | 3 | 7 | 5号边 |
下表 为 邻接表
下标 | first | next |
---|---|---|
1 | 5 | -1 |
2 | 4 | -1 |
3 | -1 | 1 |
4 | 2 | -1 |
5 | 3 |
对这个邻接表做一下解释 :
左边为起始边 然后右边为以起始边 开始的一系列边
例如如果要找 以一号点为起点的所有边的终点为 5 3 1
产生过程 :
先把first 存储的第一个终边 存放到 next 数组 然后 将 first数组 的值修改为 现在所在的边号
这样 就使得 现在所存的终边 即作为终边值又作为 查找下一个终边时候的索引 这样 就构成了一个终边链
感觉有些地方还是有些啰嗦 后续还会更新的