设一个加权有向图中有负权重的边,设图中存在若干负环,设计算法找出其中一个负环。
找负环考虑的是bellman-Ford算法,它是用来找最小生成树的算法,算法思想是循环|V-1|次,每次都对所有的边进行relax操作,最后再一次relax操作,如果还能被relax,就说明存在负环。
要找到负环,就从这条边的端点开始。
重要的具体实现代码:
1:存储
用hashMap存储邻接链表结构的图:pair里面存的是to顶点和权重。
static HashMap<Integer, LinkedList<Pair<Integer, Integer>>> graph = new HashMap<>();//存储原始图
static Scanner scanner = new Scanner(System.in);
static int v, e;//顶点数,边数
static int[] pre, dis, visted;//存储前节点和距离
2:初始化
初始化有一个要注意的就是dis数组初始化的时候不能用Integer.max_VALUE. 这样在执行relax的加法操作的时候会溢出,结果奇奇怪怪的。
public static void ini() {
System.out.println("请输入顶点数:");
v = scanner.nextInt();
System.out.println("请输入边数和各条边以及权重:");
e = scanner.nextInt();
pre = new int[v];
dis = new int[v];
for(int i=0; i<v; i++) {
pre[i] = -1;
dis[i] = 1000;//这里要注意
}
for(int i=0; i<e; i++) {
graph.put(i, new LinkedList<>());
}
for(int i=0; i<e; i++) {
int from = scanner.nextInt();
int to = scanner.nextInt();
int weight = scanner.nextInt();
graph.get(from).add(new Pair(to, weight));//有向图
//graph.get(to).add(new Pair(from, weight));//双向添加
//DFS(from, visited, graph);
}
printGraph(graph);
}
3:relax(u,v)操作
如果v.d > u.d+w(u,v),更新v.d = u.d+w(u,v)。
public static void relax(int i, Pair<Integer, Integer> pair) {
int from = i, to = pair.getKey(), weight = pair.getValue();
int newD = dis[from]+weight;
if(dis[to] > newD) {
dis[to] = newD;
pre[to] = from;
}
}
4:主要的bellman-Ford算法
public static void bellman_ford(HashMap<Integer, LinkedList<Pair<Integer, Integer>>> graph, int s) {
dis[s] = 0;//源点到自己的距离是0;
for(int i=0; i<v-1; i++) {
for(int j=0; j<v; j++) {
for(Pair<Integer, Integer> pair:graph.get(j)) {//每一个点的linkedList
relax(j, pair);
}
}
}
//验证有无负环
for(int j=0; j<v; j++) {
for(Pair<Integer, Integer> pair:graph.get(j)) {//每一个点的linkedList
if(dis[pair.getKey()] > dis[j]+pair.getValue()) {//
findCir(pair.getKey());
System.exit(0);
}
}
}
}
5:根据找到的可以relax的边,找出负环
//根据点找负环,一直找pre,直到找到跟起点重合,则为一个负环
public static void findCir(int u) {
List<Integer> res = new LinkedList<>();
int end = pre[u];
//System.out.println(u+" ");
for(int a:pre)
System.out.print(a+" ");
while(true) {
if(end==-1)
continue;
if(res.indexOf(end)!=-1)//查找到了
break;
res.add(end);
end = pre[end];
}
if(res.size()>0) {
System.out.println("图中存在负环:");
for(int i=0; i<res.size(); i++)
System.out.print(res.get(i)+"-> ");
} else
System.out.println("图中没有负环");
}
至此结束。
可以优化的地方:可以在bellman-Ford最后那个relax的地方做个操作,不让其终止,而是继续,这样可以找出所有的负环。
我总觉得这个算法是存在问题的,一时又找不出哪里有毛病,望看到的不吝赐教。
附带源码:
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import javax.swing.text.html.HTMLDocument.HTMLReader.PreAction;
import javafx.util.Pair;
import sun.awt.image.ImageWatched.Link;
public class test4 {
static HashMap<Integer, LinkedList<Pair<Integer, Integer>>> graph = new HashMap<>();//存储原始图
static Scanner scanner = new Scanner(System.in);
static int v, e;//顶点数,边数
static int[] pre, dis, visted;//存储前节点和距离
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void ini() {
System.out.println("请输入顶点数:");
v = scanner.nextInt();
System.out.println("请输入边数和各条边以及权重:");
e = scanner.nextInt();
pre = new int[v];
dis = new int[v];
for(int i=0; i<v; i++) {
pre[i] = -1;
dis[i] = 1000;
}
for(int i=0; i<e; i++) {
graph.put(i, new LinkedList<>());
}
for(int i=0; i<e; i++) {
int from = scanner.nextInt();
int to = scanner.nextInt();
int weight = scanner.nextInt();
graph.get(from).add(new Pair(to, weight));//有向图
//graph.get(to).add(new Pair(from, weight));//双向添加
//DFS(from, visited, graph);
}
printGraph(graph);
}
//输出图
public static void printGraph(HashMap<Integer, LinkedList<Pair<Integer, Integer>>> graph) {
for(int i=0; i<v; i++) {
System.out.print("\n"+i+" : ");
for(Pair<Integer, Integer> p : graph.get(i)) {
System.out.print(" "+i+"->"+p.getKey()+" "+p.getValue());//i->p 3
}
}
System.out.println();;
}
//relax操作
public static void relax(int i, Pair<Integer, Integer> pair) {
int from = i, to = pair.getKey(), weight = pair.getValue();
int newD = dis[from]+weight;
if(dis[to] > newD) {
dis[to] = newD;
pre[to] = from;
}
}
//beelman-ford
public static void bellman_ford(HashMap<Integer, LinkedList<Pair<Integer, Integer>>> graph, int s) {
dis[s] = 0;//源点到自己的距离是0;
for(int i=0; i<v-1; i++) {
for(int j=0; j<v; j++) {
for(Pair<Integer, Integer> pair:graph.get(j)) {//每一个点的linkedList
relax(j, pair);
}
}
}
//验证有无负环
for(int j=0; j<v; j++) {
for(Pair<Integer, Integer> pair:graph.get(j)) {//每一个点的linkedList
if(dis[pair.getKey()] > dis[j]+pair.getValue()) {//
findCir(pair.getKey());
System.exit(0);
}
}
}
}
//根据点找负环,一直找pre,直到找到跟起点重合,则为一个负环
public static void findCir(int u) {
List<Integer> res = new LinkedList<>();
int end = pre[u];
//System.out.println(u+" ");
for(int a:pre)
System.out.print(a+" ");
while(true) {
if(end==-1)
continue;
if(res.indexOf(end)!=-1)//查找到了
break;
res.add(end);
end = pre[end];
}
if(res.size()>0) {
System.out.println("图中存在负环:");
for(int i=0; i<res.size(); i++)
System.out.print(res.get(i)+"-> ");
} else
System.out.println("图中没有负环");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ini();//初始化
bellman_ford(graph, 0);
}

本文介绍了如何运用Bellman-Ford算法在加权有向图中寻找负权重环。算法通过|V-1|次迭代检查所有边,如果在最后一步仍能松弛,则表明存在负环。文章提供了关键的Java代码实现,并讨论了初始化、relax操作以及可能的优化方法。作者对于算法的某个方面持有疑问,希望读者提供帮助。
743

被折叠的 条评论
为什么被折叠?



