最短路算法
这里只总结两种最短路算法,第一是:Dijkstra,第二是:SPFA;
这两种算法都用了队列优化,Dijkstra是优先队列,SPFA是普通队列;
Dijkstra
先介绍第一种,也是用的最多的一种最短路算法:Dijkstra;
这种算法的思想就是先根据给定的起点 s ,找到这个点 s 所能连的点,更新一下所能连到的点的路径,然后放入优先队列里面;优先队列维护两个东西,一个是起点 s 到这个点的路径权值,一个是这个点的标号;
这里重点要说的是对于负权边的处理,对于负权边,Dijkstra是没法处理的,
比如:
1–2 权值为1
2–3 权值为-3
3–1 权值为2
根据Dijkstra的每个点只遍历一遍,所以1–2的最短路是1,而不是-1;当然保证最短路每条边只走一次;
Dijkstra模板:
struct queue_element
{
int x,y,dis_value;
queue_element(int x_,int y_,int dis_value_):
x(x_),y(y_),dis_value(dis_value_){}
bool operator < (const queue_element &other) const
{
return dis_value>other.dis_value;
}
};
inline void dijkstra()//可以不带参数,也可以把起点带进来
{
priority_queue<element> q;//优先队列大法吼
q.push(element(1,0));//把起点压进去
while(!q.empty())//不空就说明还有点没搜完
{
element k=q.top();//取出队首
q.pop();
if(vis[k.node])//如果已经在集合中(被搜到过)
continue;//扔掉
vis[k.node]=1;//标记
dis[k.node]=k.value;//存下最短路(由于优先队列的排序已经相当于完成了松弛,所以这就是答案)
for(vector<edge>::iterator it=v[k.node].begin();it!=v[k.node].end();++it)//用指针遍历邻接表
q.push(element(it->node,it->weight+k.value));//松弛
}
}
SPFA
这里先说一下,除非有负权边和负环要用SPFA,其他一概不用它;
这个算法和 Dijkstra 的区别还是挺大的:
- queue而不是priority_queue;
- 当一个点出队时,要擦掉它的标记,保证它可以被再次遍历到;
- 统计每个点的遍历次数,如果大于等于 n 次,说明有负环;
这里不在深入解释这个算法:
bool SPFA()
{
queue<int> q;//队列
memset(dis,127/3,sizeof(dis));//初始化一个很大的数
dis[s]=0;//原点最短路是0
q.push(s);//原点入队
vis[s]=1;//标记入队
while(!q.empty())//队列不空说明没跑完
{
int k=q.front();//取出队首
q.pop();
vis[k]=0;//擦去标记
++cnt[k];//统计次数
if(cnt[k]>=n)//如果超过n-1说明有负环
return 0;//报错
for(vector<edge>::iterator it=v[k].begin();it!=v[k].end();++it)//邻接表遍历相连的边
if(dis[it->node]>dis[k]+it->weight)//如果可以松弛
{
dis[it->node]=dis[k]+it->weight;//松弛
if(!vis[it->node])//如果被松弛的点不在队列里
{
vis[it->node]=1;//标记入队
q.push(it->node);//入队
}
}
}
return 1;//顺利完成
}