1、Dijkstra
dijkstra算法的思想是一种贪心的思想,它求得是单源最短路的问题,设置一个顶点的集合,初始集合是源点,然后从源点出发,找到最近的点把该点纳入这个集合,然后再从这个集合出发,把离源点最近的点纳入集合,直到把所有的点纳入集合。最后的到的即是从源点出发到其它所有的点的最短距离。
举个例子:
求1到8的最短距离。我们用一个集合G来代表点集,初识G{ },然后d[]数组代表其它点到1的距离,初始d[1]=0,d[>1]=inf表示还没确定。
第一轮,把1加入G集合,G{1},更新与1相邻的点。d[2]=2,d[4]=1,d[6]=3,其它还是inf
第二轮,选择到源点最短距离的点,即4,把它并入G{1,4},更新
d[2]=min{d[2],d[4]+map[4][2]},d[2]=2;
d[6]=min{d[6],d[4]+map[4][6]},由于map[4][6]=inf,所以d[4]=3;
d[7]=min{d[7],d[4]+map[4][7]},d[7]=3;
第三轮,选择到源点最近的点,即2点,然后如上更新。
。。。。。。
最后:
G{1,2,3,4,5,6,7,8},图中所选的路径既是到所有点的最短路径。
2、floyd
这个算法复杂度O(n*n*n),所以对于点比较少的时候还行。它的思想是这样的:对于A,B节点之间的路径,而该路径对于k节点无非就是经过与不经过的问题。所以我们定义dis[a,b]为a到b的最短距离,我们可以枚举所有的k节点,在经过或不经过的所有k点的之中比较,得出a到b之间的最短距离。若符合dis[a,b]>dis[a,k]+dis[k,b],那么我们就更新
dis[a,b]=dis[a,k]+dis[k,b];
代码大概如下:
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(Map[i][k]==inf||Map[k][j]==inf) continue;
if(Map[i][j]>Map[i][k]+Map[k][j]||Map[i][j]==inf){
Map[i][j]=Map[i][k]+Map[k][j];///通过不断的松弛操作,把节点更新了
}
}
这里要注意了,k循环必须放在外面,因为如果放在最内的循环的话,每次更新的时候dis[i,j]就马上确定下来了,这样做法是错误的:
可以看下面一个例子:
如果放在内循环,那么更新得到的dis[A,B]将是9,但正确的是6。放在内循环的话还没来得及更新dis[B,D]这两点就把A,B确定下来了,得到的答案显然是不对的。若放在外循环的话,每一轮更新都会用到前面求得的dis,这有动态规划的思想,直到最后所求的就是所有点两两之间的距离都是最短的。
3、SPFA
在求最短路时如果有负权的边dijkstra等算法没有用武之地,而Bellman-Ford的复杂度有过高,而spfa可高效的解决问题。
spfa的基本是思想是:用一个d[]数组来维护其它点到源点的距离,用一个队列来保存要优化的点,每次从队列里出一个点,并更新其它与该点相连的点到源点的距离,更新的这些点如果在队列中,则不管,如果不在队列中,则将其加入到队列里面。这样不断入队出队,直到队列为空,结果就出来了。这种方法称为动态逼近法。这个算法还可以判断是否存在负环,当每个节点出队的次数超过了n次,那么存在负环。
代码如下:
int spfa(int start){
queue<int >Q;
Q.push(start);///首先得队列里的是源点
low[start]=0;///初识化源点到源点的距离
vis[start]=true;///表示源点在队列中
while(!Q.empty()){//cout<<"sdf";
int temp=Q.front();
vis[temp]=false;
outqueue[temp]++;///计算出队次数
if(outqueue[temp]>n) return false;///判断负环。
Q.pop();
for(int v=head[temp];v!=-1;v=E[v].next){
if(low[E[v].to]>low[temp]+E[v].v){
low[E[v].to]=low[temp]+E[v].v;///不断松弛
if(!vis[E[v].to])
vis[E[v].to]=true;
Q.push(E[v].to);///若更新的点不再队列,则将其纳入队列
}
}
}
return low[N];
}