您的位置:首页 > 其它

最短路径算法汇总

2016-05-17 13:43 344 查看
Dijkstra

d[0]=0;d[1~n]=INF;

1选最小的d[i];

2从i点出发所有边 松弛d[j]

伪代码:

for(1~n)

{

所有未标记的点选出d最小的点x;

标记x点走过;

i点出发所有边 更新d[y]=min(d[y],d[x]+w[x][y]); // <x,y>属于E;

}

1)简易版:O(n*n)

对边编号:u[i],v[i] 表示第i边连通哪两点 v[i]为i点标记;

memset(v,0,sizeof(v));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
int x,m=INF;
for(int y=0;y<n;y++) if(!v[y]&&m>=INF) m=d[y],x=y;  //选出最小的d[x];
v[x]=1;
for(int y=0;y<n;y++) d[y]=min(d[y],d[x]+w[x][y]);   //更新d[y];
}


打印路径:将 for(int y=0;y<n;y++) d[y]=min(d[y],d[x]+w[x][y]);

换为 for(int y=0;y<n;y++) if(d[y]>d[x]+w[x][y]) d[y]=d[x]+w[x][y],path[y]=x;

2)优化版 O(m*logn)

对点编号 vector<int> G[maxn]; 保存邻接表;

优先队列取出d[i]最小的,就不用O(n)找出d[i]最小的了;

struct Edge
{
int from,to,dist;
Edge(int u,int v,int w) : from(u),to(v),dist(w) {}
};
struct HeapNode
{
int d,u;
bool operator < (const HeapNode& rhs) const {
return d<rhs.d;
}
}
vector<int> G[maxn]; //G[u][i] 表示u点连接的边的编号;
vector<Edge> edges; //保存对应边的编号的一切信息
bool vis[maxn]; //
int d[maxn],path[maxn];
void dijkstra(int s)
{
priority_queue<HeadNode> Q;
Q.push(HeadNode(0,s));
for(int i=0;i<n;i++) d[i]=INF,vis[i]=false;
d[s]=0;
while(!Q.empty())
{
HeapNode x=Q.top();Q.pop();
int u=x.u;
if(vis[u]) continue;
vis[u]=true;
for(int i=0;i<G[u].size();i++)
{
Edge e=edges[G[u][i]];
if(d[e.to]>d[e.from]+e.dist)
{
d[e.to]=d[e.from]+e.dist;
path[e.to]=G[u][i];
Q.push(HeapNode(e.to,e.dist));
}
}
}
}
打包成下面
struct Dijkskra
{
int n,m;
vector<int> G[maxn]; //G[u][i] 表示u点连接的边的编号;
vector<Edge> edges; //保存对应边的编号的一切信息
bool vis[maxn]; //
int d[maxn],path[maxn];
void init(int n)
{
this->n=n;
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int dist)
{
edges.push_back(from,to,dist);
m=edges.size();
G[from].push_back(m-1);
}
void dijkstra(int s)
{
priority_queue<HeadNode> Q;
Q.push(HeadNode(0,s));
for(int i=0;i<n;i++) d[i]=INF,vis[i]=false;
d[s]=0;
while(!Q.empty())
{
HeapNode x=Q.top();Q.pop();
int u=x.u;
if(vis[u]) continue;
vis[u]=true;
for(int i=0;i<G[u].size();i++)
{
Edge& e=edges[G[u][i]];
if(d[e.to]>d[e.from]+e.dist)
{
d[e.to]=d[e.from]+e.dist;
path[e.to]=G[u][i];
Q.push(HeapNode(e.to,e.dist));
}
}
}
}
}


Bellman-Ford

对每个已更新过的节点都更新其相连的节点;

最短路最多经过n-1点(起点不算),经过m条边;

伪码描述

for(1~n-1) //最多检查n-1次

for(1~m) //每次每条边都要检查

{

if(x点已经更新) 更新周围的点;

}

因为Dijkstra是每次取出最小d[i],当存在负权就会出现问题;

而使用Bellman-Ford只是随意取出相邻边所以不会出现问题;

但正是因为是随意取出相邻边,所以对于每个节点要多次检查;

即 每个节点可以多次进入并检查该节点是否能更新 ,直到无法更新周围点;

对于起点i,其他的点都要扫一遍n-1,对于每一点相邻的边都要走一遍m

//简易版  O(m*n)
for(int i=0;i<n;i++) d[i]=INF;
d[0]=0;
for(int k=0;k<n-1;k++)
for(int i=0;i<m;i++)
{
int x=u[i],y=v[i];
if(d[x]<INF) d[y]=min(d[y],d[y]+w[i]);
//if(x点更新过) //因为只有更新过才能更新你周围的点;
}
//优化版
队列优化  O(m*n) 但实际更快
bool inq[maxn];  //即inqueue 表示该点已经压入队列;
int cnt[maxn]; //记录每个点收录次数,当出现cnt[i]>n 则说明不存在最短路径 ;
bool Bellman-ford(int s)
{
queue<int> Q;
memset(inq,0,sizeof(inq));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++) d[i]=INF;
d[s]=0;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();Q.pop();
inq[u]=false;     //Q中取出来了,标为false;
for(int i=0;i<G[u].size();i++)
{
Edge& e=edges(G[u][i]);
if(d[u]<INF&&d[e.to]+e.dist)  //若d[u]是INF表明u点未被更新,也就不用更新其周围的点;
{
d[e.to]=d[u]+e.dist;
path[e.to]=G[u][i];
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to]=true;  //e.to点放入queue ,标识为true;
if(++cnt[e.to]>n) return false;  //图不通;
}
}
}
}
return true;
}


有三点要注意

1)每次for循环e.to只能压入队列一次,所以要用inq标识 ;

2)每个节点最多要放入队列n次(因为每个节点至少放入队列一次,若每个节点都可连通到x点,那么x点要进入队列n次)

所以cnt[e.to]>n 表明无最短路,可能存在负环;

3)每当e.to被更新过,就要放入队列更新e.to相连的点,直到所有的点都更新完,算法结束;

Floyd 多源最短路径 O(n^3)

通过一个图的权值矩阵求出它的每两点间的最短路径矩阵

i~j 要经过k点

d[0~n][0~n]=INF;
d[0][0]=0;
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(d[i][j]<INF&&d[k][j]<INF)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: