【图论】最短路径算法大全
来源:互联网 发布:插画师卤猫 知乎 编辑:程序博客网 时间:2024/05/08 16:59
Dijkstra算法
特点:单源最短路径,无负权边。
算法思路:设置一个dis[i]数组记录i到源点的最小距离。一开始只有源点的dis为0,其他全为无限大。从源点出发,更新周围与它相连的点。进行顶点次数的循环,每次寻找与源点距离最近的点,标记为已访问,并从该点出发更新相连的点的状态。根据三角不等式dis[i]+map[i][j]跟dis[j]比较,如果小于,说明经过i再到j比原来源点到j的最短距离还要短,则更新之。最后能得到源点到任意点的最短路径。
代码如下:
//djikstra算法 void djikstra(){int i,j,k;for(i=1;i<=n;i++){dis[i]=inf;//一开始所有点到源点的距离为无限大 vis[i]=0;}dis[1]=0;//将源点到自身距离设置为0for(i=1;i<=n;i++){int min=inf;for(j=1;j<=n;j++){if(!vis[j]&&dis[j]<min)// 遍历所有点,如果点没有被访问过,并且dis[j]被更新过,则保留dis[j]最小的点的下标 {min=dis[j];k=j;}}if(minc==inf)break;vis[k]=1;//将找出的dis[j]最小的点标记为已访问,这样首先一开始源点会被标记。从该点开始,进行下面的操作。for(j=1;j<=n;j++){if(map[k][j]!=inf&&!vis[j])//如果k点可以到达j点,即存在(k,j)边,则更新这条边 {if(dis[k]+map[k][j]<dis[j])//如果经过k点可以到达j点,则判断源点到j的距离是否大于经过该点再到j的距离,如果是,则更新之。通过这样不断地寻找最短路径。 {dis[j]=dis[k]+map[k][j];//到达j点的最短路径=到达k点的最短路径+(k,j)边的距离 pre[j]=k;//将j的前置设置为k } }} } }
Head优化的djikstra算法
由于普通的dijkstra算法每次寻找与源点最近的点都要遍历整个dis数组,效率有点慢,可以使用堆进行优化。将dis数组看成是最小堆,用o(logn)的时间复杂度去调整一个堆,只需要O(1)的时间就可以取出最小值。效率得到明显提高。
const int maxn=50000+5;const long long INF=0x3f3f3f3f3f;int weight[maxn];int cnt;int head[maxn];long long dis[maxn];bool vis[maxn];struct Edge{ int to;//指示边的终点 int w;//边的权重 int next;//下一条边的起始点 } edge[maxn<<1];struct Node{int u,dis;bool operator <(const Node &a) const{return dis>a.dis;// 当返回true时会更新堆,因此当新元素a的dis小于堆顶元素dis // 的时候会返回true,同时会更新堆,故此堆为小顶堆}} void Init(){ cnt=0; memset(head,-1,sizeof(head)); memset(vis,false,sizeof(vis)); for(int i=0; i<=n; i++) dis[i]=INF;}void addEdge(int u,int to,int w){ edge[cnt].to=to; edge[cnt].w=w; edge[cnt].next=head[u];//将这条边插入以u开头的链表 head[u]=cnt++;//将u这个点的head指针指向最后插入的节点,可以看成是一个头插法的链表 }void Dijkstra(int s){ Node now,next; priority_queue<Node>q;//定义一个优先队列 now.dis=0;//将当前点的距离设置为0 now.u=s; dis[s]=0; q.push(now);//将源点加入到优先队列中 while(!q.empty())//如果优先队列非空 { now=q.top();//取出队列中dis最小的元素.这里便是整个程序优化的地方,之前我们是通过遍历n个点来得到最小值,现在我们通过维护一个堆来取得。 q.pop();//将最小元素出队 if(vis[now.u])continue;//如果该点已经被访问,则跳过 int u=now.u;//否则从该点出发并将该点标记为已访问 vis[u]=true; for(int i=head[u]; i!=-1; i=edge[i].next)//遍历以u开头的整个链表 ,更新与u相连的点 { int to=edge[i].to; if(!vis[to]&&dis[u]+edge[i].w<dis[to]) { dis[to]=dis[u]+edge[i].w; next.dis=dis[to]; next.u=to; q.push(next); } } }}
Bellman-Ford算法
特点:单源最短路径,含负权边,可以找出负环,然而效率低下,一般使用队列优化过的SPFA方法.
思路:持续地进行松弛(即上文介绍的“三角不等式”),在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。
//Bellman-Ford算法 struct Edge{ int from;int to; int cost; }edge[N*2]; int ednum;bool Bellman(int start,int n){int i,j,flag;for(i=1;i<=n;i++){dis[i]=inf;//一开始所有点到源点的距离为无限大 vis[i]=0;}dis[start]=0;//将源点到自身距离设置为0for(i=1;i<n;i++)//进行|V|-1次循环{flag=0;for(j=0;j<ednum;j++){if(dis[edge[j].to]>dis[edge[j].from]+edge[j].cost){dis[edge[j].to]=dis[edge[j].from]+edge[j].costflag=1;}}if(!flag)break;//若没有松弛,则跳出 } for(i=1;i<n;i++)//进行第|V|次循环{if(dis[edge[i].u]>dis[edge[i].v]+edge[i].w)//如果第V次还能进行松弛,说明存在负环 return true;}return false;//第|V|次没有松弛,说明不存在负环。 }
SPFA(Short PathFaster Algorithm)
特点:是bellman-ford的一种队列实现,可以处理负权边,但无法处理负环。只要最短路径存在,上述SPFA算法必定能求出最小值。效率很高。与BFS形式相似,当BFS中一个点出队后就不可能再次进入,而SPFA中一个点可以被反复迭代。如果某个点进入队列的次数超过N次则存在负环
思路:初始时将源加入队列,每次从队列中取出一个元素,并对所有与它相邻的点松弛,若松弛成功,则将其入队,直到队列为空时算法结束。
int SPFA(int n,int s){ int i,head=0,tail=0,p,t; memset(vis,0,sizeof(vis)); for(i=1;i<=n;i++)dis[i]=inf;//初始化所有点的距离到源点为无限大 dis[s]=0;//将起点到起点距离设置为0 vis[s]=1;//将起点入队并标记为已访问 que[tail++]=s; while(head<tail) { p=que[head]; for(i=1;i<=pnt[p][0];i++) { t=pnt[p][i]; if(dis[p]+map[p][t]<dis[t]) { dis[t]=dis[p]+map[p][t]; if(!vis[t]) { que[tail++]=t; vis[t]=1; flag[t]++;//记录每个点进入队列的次数 if(flag[t]>n)return 0;//如果某个点进入队列次数超过n次则存在负环 } } } vis[p]=0; head++; } return 1;}
SPFA(dfs版本)
特点:判断负环更快
//SPFA dfs写法,判断负环更快int spfa_dfs(int u){ vis[u]=1; for(int k=head[u]; k!=0; k=edge[k].next) { int v=edge[k].v,w=edge[k].w; if( d[u]+w < d[v] ) { d[v]=d[u]+w; if(!vis[v]) { if(spfa_dfs(v)) return 1; } else return 1; } } vis[u]=0; return 0;}
floyd算法
特点:全源最短路径 时间复杂度o(n^3) 空间 o(n^2),边权可正可负,不适合
思路:利用动态规划的思路来寻找任意两点之间的最短路径,其状态转移方程为map[i,j]:=min{map[i,k]+map[k,j],map[i,j]}
void floyd(){ for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];}
- 【图论】最短路径算法大全
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- 最短路径算法
- nyoj 一笔画问题
- SQL内链接,外连接,交叉连接,联合连接区别详解
- Multithread download tool for massive tiny files.
- C#连接sqlserver时如何让Integrated Security=True生效
- 面向对象五大基本原则
- 【图论】最短路径算法大全
- RedHat机器opencv安装
- 实现自动构建编译javaweb项目并发布到N台服务器
- 数字签名和数字证书的学习总结
- Android-25种开源炫酷动画框架
- 用happen-before规则重新审视DCL
- 关于JVM中的分派
- synchronized 与 Lock区别
- 抛个关于nginx下载文件乱码的问题