最短路径

Floyd-Warshall

问题描述

求任意两个城市之间的最短路径,也就是求任意两点之间的最短路径。这个问题也被称为“多源最短路径”问题。(采用Floyd-Warshall算法)

基本思想:

最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转......允许经过1~n号顶点进行中转,求任意两点之间的最短路程。本题采用邻接矩阵的方法存储图。

package com.qianwei.chapter6;

import java.util.Scanner;

public class FloydWarshall {

    static int[][] e;
    static int n, m;
    static int inf = 99999999;

    public static void floyd() {
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j]) {
                        e[i][j] = e[i][k] + e[k][j];
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        e = new int[n+1][n+1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
               if (i == j) 
                   e[i][j] = 0;
               else 
                   e[i][j] = inf;
            }
        }
        for (int i = 1; i <= m; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            e[a][b] = c;
        }
        floyd();
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) 
                System.out.print(e[i][j] + " ");
            System.out.println();
        }
        sc.close();
    }
}

 

Dijkstra算法——单源最短路

问题描述

就是指定一个点,然后求出这个点到其余各个点的最短路径。

基本思想: 每次找到离源点最近的一个顶点,然后以该顶点为中心进行扩展最终得到源点到其余所有点的最短路径。

基本步骤如下:

  1. 将所有顶点分为两部分:已知最短路程的顶点集合P和未知最短路程的顶点集合Q。用book[i]表示,如果book[i]=1则表示这个顶点在集合P中,反之顶点在集合Q中。
  2. 设置源点s到自己的最短路径为0。其余按照实际情况进行设置。
  3. 在集合Q的所有定点中选择一个离源点s最近的顶点加入到集合P。并考察所有以点u为起点的边,对每一条边进行松弛操作。
  4. 重复第三步,如果集合Q为空,算法结束。最终dis数组中的值就是源点到所有顶点的最短路径。
package com.qianwei.chapter6;

import java.util.Scanner;

public class DijkstraTest1 {

    static int[][] e;
    static int[] book;
    static int[] dis;
    static int n, m;
    static int min;
    static int mark;
    static int inf = 99999999;

    public static void dijkstra() {
        for (int i = 1; i <= n-1; i++) { //循环n-1次,去掉源点
            min = inf;
            for (int j = 1; j <= n; j++) {
                if (book[j] == 0 && dis[j] < min) {
                    min = dis[j];
                    mark = j;
                }
            }
            book[mark] = 1;
            for (int j = 1; j <= n; j++) {
                if (e[mark][j] < inf) {
                    if (dis[j] > dis[mark] + e[mark][j]) 
                        dis[j] = dis[mark] + e[mark][j];
                }
            }
        }
    }
    
    public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        e = new int[n+1][n+1];
        dis = new int[n+1];
        book = new int[n+1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
               if (i == j) 
                   e[i][j] = 0;
               else 
                   e[i][j] = inf;
            }
        }
        for (int i = 1; i <= m; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            e[a][b] = c;
        }
        for (int i = 1; i <= n; i++)
            dis[i] = e[1][i];
        
        book[1] = 1;
        dijkstra();

        for (int i = 1; i <= n; i++)
            System.out.print(dis[i] + " ");
        sc.close();
    }
}

用邻接表来存储图

package com.qianwei.chapter6;

import java.util.Scanner;

public class DijkstraTest2 {

	static int n, m;
	static int[] u;
	static int[] v;
	static int[] w;
	static int[] first;
	static int[] next;
	static int[] dis;
	static int[] book;
	static int inf = 999999999;
	static int mark;
	static int min;
	
	static void dijkstra() {
		for (int i = 1; i <= n-1; i++) { //循环n-1次,去掉源点
            min = inf;
            for (int j = 1; j <= n; j++) {
                if (book[j] == 0 && dis[j] < min) {
                    min = dis[j];
                    mark = j;
                }
            }
            book[mark] = 1;
            for (int j=1;j<=n;j++) {
            	if (mark != j)
            		continue;
            	int k = first[j];
            	while (k != -1) {
            		if (dis[v[k]] > dis[mark] + w[k])
            			dis[v[k]] = dis[mark] + w[k];
            		k = next[k];
            	}
            }
        }
	}
	
	public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        u = new int[m+1];
        v = new int[m+1];
        w = new int[m+1];
        first = new int[n+1];
        next = new int[m+1];
        book = new int[n+1];
        dis = new int[n+1];
        for (int i=1;i<=n;i++)
        	first[i] = -1;
        for (int i=1;i<=m;i++) {
        	u[i] = sc.nextInt();
        	v[i] = sc.nextInt();
        	w[i] = sc.nextInt();
        	next[i] = first[u[i]];
        	first[u[i]] = i;
        }
        for (int i = 1; i <= n; i++)
        	dis[i] = inf;
        dis[1] = 0;
        int k = first[1];
    	while (k != -1) {
    		dis[v[k]] = w[k]; 
    		k = next[k];
    	}        
        book[1] = 1;
        dijkstra();

        for (int i = 1; i <= n; i++)
            System.out.print(dis[i] + " ");
        sc.close();
	}
}

 

Bellman-Ford——解决负权边

package com.qianwei.chapter6;

import java.util.Scanner;

public class BellmanFordTest1 {

	static int[] u;
	static int[] v;
	static int[] w;
	static int[] dis;
    static int n, m;
    static int inf = 999999999;

    static void bellmanFlod() {
        //在一个n个顶点的图中,任意两点之间的最短路径最多包含n-1条边
        for (int k = 1; k <= n - 1; k++) {
            for (int j = 1; j <= m; j++) {
                if (dis[v[j]] > dis[u[j]] + w[j]) {
                    dis[v[j]] = dis[u[j]] + w[j];
                }
            }
        }
    }
    
    public static void main(String[] args) {
    	Scanner sc  = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        u = new int[m+1];
        v = new int[m+1];
        w = new int[m+1];
        dis = new int[n+1];
        for (int i = 1; i <= m; i++) {
            u[i] = sc.nextInt();
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        for (int i = 1; i <= n; i++) 
            dis[i] = inf;
        dis[1] = 0;
        bellmanFlod();
        for (int i = 1; i <= n; i++) 
            System.out.print(dis[i] + " ");
        sc.close();
    }
}

 

添加检测是否有负权回路功能,再添加一个变量check用来标记数组dis在本轮松弛中是否发生了变化,如果没有发生变化,则可以提前跳出循环。

package com.qianwei.chapter6;

import java.util.Scanner;

public class BellmanFordTest2 {

	static int[] u;
	static int[] v;
	static int[] w;
	static int[] dis;
    static int n, m;
    static int check, flag;
    static int inf = 999999999;

    static void bellmanFlod() {
        //在一个n个顶点的图中,任意两点之间的最短路径最多包含n-1条边
        for (int i = 1; i <= n - 1; i++) {
        	check = 0;
        	for (int j = 1; j <= m; j++) {
                if (dis[v[j]] > dis[u[j]] + w[j]) {
                    dis[v[j]] = dis[u[j]] + w[j];
                    check = 1;
                }
            }
            //松弛完毕后检测数组dis是否有更新
        	//如果数组dis没有更新,提前退出循环结束算法
        	if (check == 0)
        		break;
        }
        //检测负权回路
        flag = 0;
        for (int i = 1; i <= m; i++) {
            if (dis[v[i]] > dis[u[i]] + w[i]) 
                flag = 1;
        }
    }
    
    public static void main(String[] args) {
    	Scanner sc  = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        u = new int[m+1];
        v = new int[m+1];
        w = new int[m+1];
        dis = new int[n+1];
        for (int i = 1; i <= m; i++) {
            u[i] = sc.nextInt();
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        for (int i = 1; i <= n; i++) 
            dis[i] = inf;
        dis[1] = 0;
        bellmanFlod();
        if (flag == 1) {
            System.out.println("此图含有负权回路");
        } else {
            for (int j = 1; j <= n; j++) {
                System.out.print(dis[j] + " ");
            }
        }
        sc.close();
    }
}

 

Bellman-Ford的队列优化

每次仅对最短路径估计值发生变化了的顶点的所有出边执行松弛操作。

package com.qianwei.chapter6;

import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;

public class BellmanFordTest3 {

	static int n, m;
	static int[] u;
	static int[] v;
	static int[] w;
	static int[] first;
	static int[] next;
	static int[] dis;
	static int[] book;
	static int inf = 999999999;
    static Deque<Integer> queue = new LinkedList<Integer>();

    static void bellomanFlod() {
        while (!queue.isEmpty()) {
            int k = first[queue.peek()];
            while (k != -1) {
                if (dis[v[k]] > dis[u[k]] + w[k]) {
                    dis[v[k]] = dis[u[k]] + w[k];
                    if(book[v[k]] == 0) {
                        queue.offer(v[k]);
                        book[v[k]] = 1;
                    }
                }
                k = next[k];
            }
            book[queue.peek()] = 0;
            queue.poll();
        }
    }
    
    public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        u = new int[m+1];
        v = new int[m+1];
        w = new int[m+1];
        first = new int[n+1];
        next = new int[m+1];
        book = new int[n+1];
        dis = new int[n+1];
        for (int i = 1; i <= n; i++) 
            dis[i] = inf;
        dis[1] = 0;
        for (int i = 1; i <= n; i++) 
            first[i] = -1;
        for (int i = 1; i <= m; i++) {
            u[i] = sc.nextInt();
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
            next[i] = first[u[i]];
            first[u[i]] = i;
        }
        queue.offer(1);
        book[1] = 1;
        bellomanFlod();
        for (int i = 1; i <= n; i++) 
            System.out.print(dis[i] + " ");
        sc.close();
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值