bellman-Ford查找输出一个负环(java)

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

设一个加权有向图中有负权重的边,设图中存在若干负环,设计算法找出其中一个负环。

找负环考虑的是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);
}
 

 

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值