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,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

贝尔曼-福特算法(Bellman-Ford Algorithm)是一种用于寻找有向加权图中节点对之间最短路径的动态规划方法。在处理多段最短路径问题时,每个边都有独立的权值,需要考虑所有边的影响。 在Java中,你可以用数组来表示图的邻接矩阵,并用两个循来运行贝尔曼-福德算法两次,一次是为了查找可能存在的,一次则是计算实际的最短路径。以下是简单的步骤: 1. **初始化**:创建一个长度为10的数组dist,将所有距离设为无穷大,除了源点0,其dist值设为0。 ```java int[][] graph = { // 边的权重,这里假设从0到i的距离为1 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 0, 10, 11, 12, 13, 14, 15, 16, 17}, ... // 其他边的权重 }; int[] dist = new int[10]; Arrays.fill(dist, Integer.MAX_VALUE); dist[0] = 0; ``` 2. **外层循**:运行V-1次(V为节点数),每次更新dist数组,如果发现通过某个中间节点可以到达更短的路径,则更新该节点的距离。 3. **内层循**:对于每条边(u, v),检查是否存在一个松弛操作,即`dist[v] > dist[u] + graph[u][v]`。如果有,说明可能存在,算法会在这个阶段结束并报告错误。如果没有,就继续遍历下一跳节点。 ```java for (int i = 0; i < V - 1; i++) { for (int u = 0; u < V; u++) { for (int v : neighborsOf[u]) { // 邻居集合neighborsOf[u] if (dist[u] != Integer.MAX_VALUE && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } } ``` 4. **最后一步**:检查是否存在,因为如果有的话,在V-1次迭代后仍然会有更新。如果没有,算法完成,dist数组存储的就是从0到9的最短路径。 ```java // 检查 for (int u = 0; u < V; u++) { for (int v : neighborsOf[u]) { if (dist[u] + graph[u][v] < dist[v]) throw new IllegalArgumentException("Graph contains negative weight cycle!"); } } // 输出最短路径 System.out.println("从0到9的最短路径和权值: "); for (int i = 1; i < 10; i++) { System.out.printf("0到%d: %d\n", i, dist[i]); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值