您的位置:首页 > 其它

图的路径:最短路-四种算法

2016-02-02 16:12 295 查看
先说前三种,主要解决的问题是:“求a点到b点的最短路”,解决方法是:用以下三种算法中的一种,求出a点到其他所有点的最短路,即可知道a点到b点的最短路。

前三种最短路算法是:Dijkstra(及优先队列优化),Bellman-Ford,SPFA(实际上就是BF的优化版)。

Dijkstra算法:(要求边权只能大于等于0)

未优化O(N2):

贪心算法:每次找到当前距离起始点(a点)最短的点扩展更新,且该点不会再被更新

数据结构:开一个二维数组cost[i][j]来存储各点之间的距离(邻接矩阵),INF表示无通路,x点到x点之间距离为0;用一维数组lowcost[i]表示最短路径,用vis[i]保存路径中某点是否已经走过。

Dij+邻接矩阵模板如下:

constintINF=10e7;
constintMAXN=1010;
intk,minn;
intcost[MAXN][MAXN];
intlowcost[MAXN];
boolvis[MAXN];

voiddij(intn,intstart)
{
for(inti=1;i<=n;i++)
lowcost[i]=INF,vis[i]=0;
lowcost[start]=0;

for(intj=1;j<=n;j++)
{
k=-1,minn=INF;
for(inti=1;i<=n;i++)
{
if(!vis[i]&&lowcost[i]<minn)
{minn=lowcost[i];k=i;}
}
if(k==-1)break;//这里需要对k进行判定,是因为有可能在k未赋值的情况下使用了vis[k]
vis[k]=1;
for(inti=1;i<=n;i++)
if(!vis[i]&&cost[k][i]>=0&&lowcost[k]+cost[k][i]<lowcost[i])
lowcost[i]=lowcost[k]+cost[k][i];
}
}


优先队列优化O(NlogN)://且对于稠密图,使用优先队列优化的Dijkstra算法更有效

将原先未优化版的第一个O(N)优化,用优先队列来维护,复杂度变为O(logN),故总复杂度为O(NlogN)。

Dij+邻接表+优先队列模板如下:

constintMAXN=1010;
constintINF=10e8;

structnode
{
intv,val;
node(int_v=0,int_val=0):v(_v),val(_val){}
//等价于node(int_v,int_val){v=_v;val=_val;}
booloperator<(constnode&a)const
{
if(val==a.val)returnv<a.v;
elsereturnval>a.val;
}
};

vector<node>E[MAXN];
boolvis[MAXN];

voidDijkstra(intlowcost[],intn,intstart)
{
intlen,u,v,cost;
nodeqtemp;
for(inti=1;i<=n;i++)
lowcost[i]=INF;vis[i]=0;
lowcost[start]=0;
//用优先队列优化
priority_queue<node>que;
que.push(node(start,0));
while(!que.empty())//类似于BFS
{
qtemp=que.top();que.pop();
u=qtemp.v;
if(vis)
continue;
vis[u]=1;len=E[u].size();

for(inti=0;i<len;i++)
{
v=E[u][i].v;cost=E[u][i].cost;
if(!vis[v]&&lowcost[v]>lowcost[u]+cost)
{
lowcost[v]=lowcost[u]+cost;
que.push(node(v,lowcost[v]));
}
}
}
}


Bellman-Ford算法:(边权可以为负权,且可用于判断图是否存在负权回路)


原理是进行[u]松弛
操作,最多N-1次,复杂度O(NM)。(松弛一次的复杂度为O(M))

步骤:

1.初始化所有点,每个点保存一个值,表示从原点到此点的距离,INF表示两点之间不连通;

2.进行循环,下标从1到n-1(n为图中点的个数),复杂度O(N),在循环内部遍历所有的边,进行松弛,复杂度O(M);

3.跳出循环后,遍历途中所有的边(edge(u,v)),判断是否存在:

lowcost(v)>lowcost(u)+cost(u,v)的情况,若存在,则返回false,表示图中存在负权回路,无法求得最短路径。

BF+邻接表模板如下:

constintMAXN=1010;
constintINF=10e8;

structedge
{
intu,v,cost;
edge(int_u=0,int_v=0,int_cost=0)u(_u),v(_v),cost(_cost){}
};

vector<edge>E;

boolBellman_Ford(intlowcost[],intn,intstart)
{//返回值用于判断是否有负环,false说明有负环,即无法求出最短路
boolok;
intu,v,cost;
intlen=E.size();

for(inti=1;i<=n;i++)
lowcost[i]=INF;
lowcost[start]=0;

for(inti=0;i<n-1;i++)
{
ok=0;
for(intj=0;j<len;j++)
{
u=E[j].u;v=E[j].v;cost=E[j].cost;
if(lowcost[v]>lowcost+cost)//进行松弛
{
lowcost[v]=lowcost[u]+cost;
ok=1;
}
}
if(!ok)
returntrue;
}
for(intj=0;j<len;j++)
if(lowcost[E[j].v]>lowcost[E[j].u]+E[j].cost)
returnfalse;
returntrue;
}

//加边操作
inlinevoidadd_edge(intu,intv,intc)
{
E.push_back(edge(u,v,c));
}


SPFA算法:


是优化后的BF算法,在进行松弛的时候,每次只对上一次改变过的点进行松弛。

SPFA+邻接表模板如下:

constintMAXN=1010;
constintINF=10e8;

structedge
{
intv,cost;
edge(int_v=0,int_cost=0)v(_v),cost(_cost){}
};

vector<edge>E[MAXN];
boolvis[MAXN];
intcouNode[MAXN];

boolSPFA(intlowcost[],intn,intstart)
{//返回值用于判断是否有负环,false说明有负环,即无法求出最短路
queue<int>que;
intu,v,cost;
intlen=E.size();

for(inti=1;i<=n;i++)
lowcost[i]=INF,vis[i]=0,couNode[i]=0;
lowcost[start]=0;vis[start]=0;couNode[start]=1;
que.push(start);

for(!que.empty())
{
u=que.front();que.pop();
vis[u]=0;len=E[u].size();

for(inti=0;i<len;i++)
{
v=E[i].v;cost=E[i].cost;
if(lowcost[v]>lowcost[u]+cost)//进行松弛
{
lowcost[v]=lowcost[u]+cost;
if(!vis[v])
{
vis[v]=1;
++couNode[v];
que.push(v);
if(couNode[v]>n)
returnfalse;
}
}
}
}
returntrue;
}

//加边操作
inlinevoidadd_edge(intu,intv,intc)
{
E[u].push_back(edge(v,c));
}


Floyd算法:


第四种比较特殊又好用的算法是folyd算法;

若要求图上任意两点之间的最短路(而不是给了确切的起点a),就用Folyd算法;此算法代码短,复杂度O(N3)用的是动态规划思想。

且Floyd算法也可用于[u]判环
,若lowcost[a][a]有值且不为INF,那么说明图中存在从a点到a点的一个环。

Floyd+邻接矩阵模板如下:

constintINF=10e7;
constintMAXN=1010;
intlowcost[MAXN][MAXN];//记录a点到b点之间的最短路

voidfloyd(intn)
{
for(intk=1;k<=n;k++)//千万注意k要在最外层循环,否则会出错
for(inti=1;i<=n;i++)
for(intj=1;j<=n;j++)
lowcost[i][j]=min(lowcost[i][j],lowcost[i][k]+lowcost[k][j]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐
章节导航