负权值最短路径Bellman-Ford算法
1. 简述
在解决图中的单元最短路径时,经常使用到的算法是Dijkstra算法,但是如果图中的边的权值为负数的话,Dijkstra算法就不适用了,此时需要一种新的算法:Bellman-Ford算法
松弛操作:在Dijkstra算法中,会有一个更新距离的操作,该操作就是一个松弛操作,在Dijkstra算法中,当一个新的节点v被加入集合S时,会根据该节点来更新源点到与v可以到达的其他点的距离。
Dijkstra算法以贪心法选取未被处理的具有最小权值的节点,然后对其出边进行松弛操作
Bellman-Ford算法对所有边都进行松弛操作,共n -1次
负环:一个环路,环路上的边的权值之和为负数
当一个图中存在负环时,不存在最短路径,因此可以反复经过负环,使得权值不断减少
如和判断负环的存在?
对图G进行V-1次松弛操作,如果再进行第V次操作时还能松弛(更新),说明存在负环
2. 算法的基本实现
package algorithm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
public class BellmanFordDemo {
double[] dis;
int[] pre;//保存路径上的前驱节点
public boolean solve(ArrayList<Edge> G,int start,int n) {
dis=new double[n];
pre=new int[n];
Arrays.fill(dis, Integer.MAX_VALUE);
dis[start]=0;
for(int i=1;i<n;i ) {
boolean flag=false;
for(Edge edge:G) {//对所有的边进行松弛操作
int from=edge.from;
int to=edge.to;
double weight=edge.weight;
if(dis[to]>dis[from] weight) {//松弛操作 更新s->to的路径长度
dis[to]=dis[from] weight;
pre[to]=from;
flag=true;
}
}
if(!flag)//在某一轮循环中如果没有发生松弛 说明松弛操作提前结束
return false;//松弛完毕且无环
}
for(Edge edge:G) {
int from=edge.from;
int to=edge.to;
double weight=edge.weight;
if(dis[to]>dis[from] weight) {
return true;//松弛完V-1次后如果还能松弛 说明存在负权值的环
}
}
return false;//无负权值的环
}
public LinkedList<Integer> path(int start,int end)
{
LinkedList<Integer> st=new LinkedList<>();
for(int x=end;x!=start;x=pre[x])
st.push(x);
st.push(start);
return st;
}
public static void main(String[] args) {
int V=8,E=15;
BellmanFordDemo obj=new BellmanFordDemo();
ArrayList<Edge> edges=new ArrayList<>();
Edge edge1=new Edge(4,5,0.35);
Edge edge2=new Edge(5,4,0.35);
Edge edge3=new Edge(4,7,0.37);
Edge edge4=new Edge(5,7,0.28);
Edge edge5=new Edge(7,5,0.28);
Edge edge6=new Edge(5,1,0.32);
Edge edge7=new Edge(0,4,0.38);
Edge edge8=new Edge(0,2,0.26);
Edge edge9=new Edge(7,3,0.39);
Edge edge10=new Edge(1,3,0.29);
Edge edge11=new Edge(2,7,0.34);
Edge edge12=new Edge(6,2,-1.20);
Edge edge13=new Edge(3,6,0.52);
Edge edge14=new Edge(6,0,-1.40);
Edge edge15=new Edge(6,4,-1.25);
edges.add(edge1);
edges.add(edge2);
edges.add(edge3);
edges.add(edge4);
edges.add(edge5);
edges.add(edge6);
edges.add(edge7);
edges.add(edge8);
edges.add(edge9);
edges.add(edge10);
edges.add(edge11);
edges.add(edge12);
edges.add(edge13);
edges.add(edge14);
edges.add(edge15);
int start=0;
boolean hasNegativeCycle=obj.solve(edges, start, V);
if(hasNegativeCycle) {
System.out.println("negative cycle exists");
}else {
for(int i=0;i<V;i ) {
System.out.print(start "-->" i " : " obj.dis[i]);
System.out.println(" path:" obj.path(start, i));
}
}
}
}
class Edge{
int from,to;
double weight;
public Edge(int from, int to, double weight) {
super();
this.from = from;
this.to = to;
this.weight = weight;
}
}
//时间复杂度 O(VE)
3. 优化
松弛操作必定只会发生在最短路径松弛过的前驱节点上,用一个队列记录松弛过的节点,可以避免冗余计算
package algorithm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
public class BellmanFordDemo {
double[] dis;
int[] pre;//保存路径上的前驱节点
boolean[] vis;
int[] cnt;
public boolean solve(ArrayList<Edge>[] G,int start,int n) {
dis=new double[n];
pre=new int[n];
vis=new boolean[n];
cnt=new int[n];//记录某个节点的入队次数
Arrays.fill(dis, Integer.MAX_VALUE);
Queue<Integer> q=new LinkedList<>();
q.offer(start);
dis[start]=0;
vis[start]=true;
cnt[start] ;
while(!q.isEmpty()) {
int x=q.poll();
vis[x]=false;//标记x不在队列中 因为会存在a<=>b这种情况 b可以再次达到a
for(Edge edge:G[x]) {
int to=edge.to;
if(dis[to]>dis[x] edge.weight) {
dis[to]=dis[x] edge.weight;
pre[to]=x;
if(!vis[to]) {
if( cnt[to]>=n)//某个节点的入队次数如果大于等于n说明有负环
return true;
vis[to]=true;
q.offer(to);//标记to在队列中
}
}
}
}
return false;//无负权值的环
}
public LinkedList<Integer> path(int start,int end)
{
LinkedList<Integer> st=new LinkedList<>();
for(int x=end;x!=start;x=pre[x])
st.push(x);
st.push(start);
return st;
}
public static void main(String[] args) {
int V=8,E=15;
BellmanFordDemo obj=new BellmanFordDemo();
ArrayList<Edge> edges=new ArrayList<>();
Edge edge1=new Edge(4,5,0.35);
Edge edge2=new Edge(5,4,0.35);
Edge edge3=new Edge(4,7,0.37);
Edge edge4=new Edge(5,7,0.28);
Edge edge5=new Edge(7,5,0.28);
Edge edge6=new Edge(5,1,0.32);
Edge edge7=new Edge(0,4,0.38);
Edge edge8=new Edge(0,2,0.26);
Edge edge9=new Edge(7,3,0.39);
Edge edge10=new Edge(1,3,0.29);
Edge edge11=new Edge(2,7,0.34);
Edge edge12=new Edge(6,2,-1.20);
Edge edge13=new Edge(3,6,0.52);
Edge edge14=new Edge(6,0,-1.40);
Edge edge15=new Edge(6,4,-1.25);
edges.add(edge1);
edges.add(edge2);
edges.add(edge3);
edges.add(edge4);
edges.add(edge5);
edges.add(edge6);
edges.add(edge7);
edges.add(edge8);
edges.add(edge9);
edges.add(edge10);
edges.add(edge11);
edges.add(edge12);
edges.add(edge13);
edges.add(edge14);
edges.add(edge15);
ArrayList<Edge>[] G=new ArrayList[V];
for(Edge edge:edges) {
int from=edge.from;
if(G[from]==null) {
G[from]=new ArrayList<>();
}
G[from].add(edge);
}
int start=0;
boolean hasNegativeCycle=obj.solve(G, start, V);
if(hasNegativeCycle) {
System.out.println("negative cycle exists");
}else {
for(int i=0;i<V;i ) {
System.out.print(start "-->" i " : " obj.dis[i]);
System.out.println(" path:" obj.path(start, i));
}
}
}
}
class Edge{
int from,to;
double weight;
public Edge(int from, int to, double weight) {
super();
this.from = from;
this.to = to;
this.weight = weight;
}
}
修改上面的边:Edge edge2=new Edge(5,4,-0.66); 则会形成负环,运行结果如下:
时间复杂度和未优化的Bellman-Ford算法相同,为O (nm );但在稀疏图上运行效率较高,为O (km ),其中k 是一个较小的常数,n是结点数,m是边数
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgehhca
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01