Java学习第二十四天 图论(广度优先,深度优先,最小生成树,最短路径)二

 最短路径

Dijkstra和单源最短路径

带权图的最短路径

Dijkstra算法

不能处理负权边,不过大量应用不依赖负权边

import java.util.Arrays;

public class Dijkstra {

    private WeightedGraph G;
    private int s;
    private int[] dis;
    private boolean[] visited;

    public Dijkstra(WeightedGraph G, int s){

        this.G = G;

        G.validateVertex(s);
        this.s = s;

        dis = new int[G.V()];//开空间
        Arrays.fill(dis, Integer.MAX_VALUE);//为dis中所有元素幅值无穷大(int中最大值
        dis[s] = 0;//s离源的位置是0

        visited = new boolean[G.V()];//给visitied开空间,这里默认是false

        while(true){
            //1.找当前没有遍历的最短路节点,存到cur里
            int cur = -1, curdis = Integer.MAX_VALUE;
            for(int v = 0; v < G.V(); v ++)
                if(!visited[v] && dis[v] < curdis){//v的dis值小于当前找到的最小dis值
                    curdis = dis[v];
                    cur = v;
                }//第一个循环必然更新cur为s,此时的循环逻辑还未正式开始

            if(cur == -1) break;
            //2.确定当前最小dis值,就算源点到它的最短路径
            visited[cur] = true;
            //3.根据这个节点的最短路径大小,更新其他节点路径的大小
            for(int w: G.adj(cur))//遍历这个cur顶点的所有相邻顶点
                if(!visited[w]){//w的最短路径如果还未得到的话,通过这层循环,对还未得到最短路径的节点进行更新
                    if(dis[cur] + G.getWeight(cur, w) < dis[w])//s-cur加cur-w<s-w,意味着找到了更短路径
                        dis[w] = dis[cur] + G.getWeight(cur, w);//更新最小路径dis[w]
                }
        }
    }

    public boolean isConnectedTo(int v){

        G.validateVertex(v);
        return visited[v];
    }

    public int distTo(int v){

        G.validateVertex(v);
        return dis[v];
    }

    static public void main(String[] args){

        WeightedGraph g = new WeightedGraph("g.txt");
        Dijkstra dij = new Dijkstra(g, 0);
        for(int v = 0; v < g.V(); v ++)
            System.out.print(dij.distTo(v) + " ");
        System.out.println();
    }
}

输入

5 8
0 1 4
0 2 2
1 2 1
1 3 2
1 4 3
2 3 4
2 4 5
3 4 1

输出

0 3 2 5 6

时间复杂度:O(V^2)

优化

import java.util.Arrays;
import java.util.PriorityQueue;


public class Dijkstra {

    private WeightedGraph G;
    private int s;
    private int[] dis;
    private boolean[] visited;

    private class Node implements Comparable<Node>{

        public int v, dis;//顶点与dis

        public Node(int v, int dis){
            this.v = v;
            this.dis = dis;
        }

        @Override
        public int compareTo(Node another){
            return dis - another.dis;
        }
    }

    public Dijkstra(WeightedGraph G, int s){

        this.G = G;

        G.validateVertex(s);
        this.s = s;

        dis = new int[G.V()];
        Arrays.fill(dis, Integer.MAX_VALUE);
        dis[s] = 0;

        visited = new boolean[G.V()];

        PriorityQueue<Node> pq = new PriorityQueue<Node>();//存顶点序号与dis值
        pq.add(new Node(s, 0));//添加源点与其dis值
        while(!pq.isEmpty()){

            //1.找当前没有遍历的最短路节点,存到cur里
            int cur = pq.remove().v;

            if(visited[cur]) continue;//如果cur已经处理过了,就接着去弄下一个

            //2.确定当前最小dis值,就算源点到它的最短路径
            visited[cur] = true;
            //3.根据这个节点的最短路径大小,更新其他节点路径的大小
            for(int w: G.adj(cur))
                if(!visited[w]){
                    if(dis[cur] + G.getWeight(cur, w) < dis[w]){
                        dis[w] = dis[cur] + G.getWeight(cur, w);//优先队列不能动态更新
                        pq.add(new Node(w, dis[w]));//所以每更新一个w,往Node中添加一个节点,方便下一次比较
                    }
                }
        }
    }

    public boolean isConnectedTo(int v){

        G.validateVertex(v);
        return visited[v];
    }

    public int distTo(int v){

        G.validateVertex(v);
        return dis[v];
    }

    static public void main(String[] args){

        WeightedGraph g = new WeightedGraph("g.txt");
        Dijkstra dij = new Dijkstra(g, 0);
        for(int v = 0; v < g.V(); v ++)
            System.out.print(dij.distTo(v) + " ");
        System.out.println();
    }
}

时间复杂度O(ElogE)

求解最短路径:pre数组

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.PriorityQueue;


public class Dijkstra {

    private WeightedGraph G;
    private int s;
    private int[] dis;
    private boolean[] visited;
    private int[] pre;

    private class Node implements Comparable<Node>{

        public int v, dis;

        public Node(int v, int dis){
            this.v = v;
            this.dis = dis;
        }

        @Override
        public int compareTo(Node another){
            return dis - another.dis;
        }
    }

    public Dijkstra(WeightedGraph G, int s){

        this.G = G;

        G.validateVertex(s);
        this.s = s;

        dis = new int[G.V()];
        Arrays.fill(dis, Integer.MAX_VALUE);

        pre = new int[G.V()];
        Arrays.fill(pre, -1);//pre默认值是-1

        dis[s] = 0;
        pre[s] = s;//源点上一个顶点是它自己
        visited = new boolean[G.V()];

        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        pq.add(new Node(s, 0));
        while(!pq.isEmpty()){

            int cur = pq.remove().v;

            if(visited[cur]) continue;

            visited[cur] = true;
            for(int w: G.adj(cur))
                if(!visited[w]){
                    if(dis[cur] + G.getWeight(cur, w) < dis[w]){
                        dis[w] = dis[cur] + G.getWeight(cur, w);
                        pq.add(new Node(w, dis[w]));
                        pre[w] = cur;//确认最短路径后,确认前一个点
                    }
                }
        }
    }

    public boolean isConnectedTo(int v){

        G.validateVertex(v);
        return visited[v];
    }

    public int distTo(int v){

        G.validateVertex(v);
        return dis[v];
    }

    public Iterable<Integer> path(int t){//可迭代的一个对象

        ArrayList<Integer> res = new ArrayList<>();//开空间
        if(!isConnectedTo(t)) return res;//是否可达t

        int cur = t;//从t开始不断往回找,获得路径
        while(cur != s){
            res.add(cur);
            cur = pre[cur];
        }
        res.add(s);

        Collections.reverse(res);
        return res;
    }

    static public void main(String[] args){

        WeightedGraph g = new WeightedGraph("g.txt");
        Dijkstra dij = new Dijkstra(g, 0);
        for(int v = 0; v < g.V(); v ++)
            System.out.print(dij.distTo(v) + " ");
        System.out.println();

        System.out.println(dij.path(3));
    }
}

Bellman-Ford算法

有方向性

松弛操作

不同松弛顺序,值有不同

但最多V-1轮,无负权环时一定得到固定解

import java.util.Arrays;

public class BellmanFord {

    private WeightedGraph G;
    private int s;
    private int[] dis;
    private boolean hasNegCycle = false;

    public BellmanFord(WeightedGraph G, int s){

        this.G = G;

        G.validateVertex(s);
        this.s = s;

        dis = new int[G.V()];
        Arrays.fill(dis, Integer.MAX_VALUE);//为dis数组赋初始值
        dis[s] = 0;

        //V-1轮的松弛操作
        for(int pass = 1; pass < G.V(); pass ++){
            for(int v = 0; v < G.V(); v ++)//遍历所有顶点
                for(int w: G.adj(v))//遍历所有顶点的相邻顶点
                    if(dis[v] != Integer.MAX_VALUE && dis[v] + G.getWeight(v, w) < dis[w])//松弛操作
                        dis[w] = dis[v] + G.getWeight(v, w);
        }

        //再做一次松弛操作,判断是否含负权环
        for(int v = 0; v < G.V(); v ++)
            for(int w : G.adj(v))
                if(dis[v] != Integer.MAX_VALUE && dis[v] + G.getWeight(v, w) < dis[w])
                    hasNegCycle = true;
    }

    public boolean hasNegativeCycle(){
        return hasNegCycle;
    }

    public boolean isConnectedTo(int v){
        G.validateVertex(v);
        return dis[v] != Integer.MAX_VALUE;
    }

    //返回去v的最短路径
    public int distTo(int v){
        G.validateVertex(v);
        if(hasNegCycle) throw new RuntimeException("exist negative cycle.");
        return dis[v];
    }

    static public void main(String[] args){

        WeightedGraph g = new WeightedGraph("g.txt");
        BellmanFord bf = new BellmanFord(g, 0);
        if(!bf.hasNegativeCycle()){
            for(int v = 0; v < g.V(); v ++)
                System.out.print(bf.distTo(v) + " ");
            System.out.println();
        }
        else
            System.out.println("exist negative cycle.");

        WeightedGraph g2 = new WeightedGraph("g2.txt");
        BellmanFord bf2 = new BellmanFord(g2, 0);
        if(!bf2.hasNegativeCycle()){
            for(int v = 0; v < g2.V(); v ++)
                System.out.print(bf2.distTo(v) + " ");
            System.out.println();
        }
        else
            System.out.println("exist negative cycle.");
    }
}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class BellmanFord {

    private WeightedGraph G;
    private int s;
    private int[] dis;
    private int[] pre;
    private boolean hasNegCycle = false;

    public BellmanFord(WeightedGraph G, int s){

        this.G = G;

        G.validateVertex(s);
        this.s = s;

        dis = new int[G.V()];
        Arrays.fill(dis, Integer.MAX_VALUE);
        dis[s] = 0;

        pre = new int[G.V()];
        Arrays.fill(pre, -1);

        for(int pass = 1; pass < G.V(); pass ++){
            for(int v = 0; v < G.V(); v ++)
                for(int w: G.adj(v))
                    if(dis[v] != Integer.MAX_VALUE &&
                       dis[v] + G.getWeight(v, w) < dis[w]){ dis[w] = dis[v] + G.getWeight(v, w);
                        pre[w] = v;//跟新完幅值
                    }
        }

        for(int v = 0; v < G.V(); v ++)
            for(int w : G.adj(v))
                if(dis[v] != Integer.MAX_VALUE &&
                   dis[v] + G.getWeight(v, w) < dis[w])
                    hasNegCycle = true;
    }

    public boolean hasNegativeCycle(){
        return hasNegCycle;
    }

    public boolean isConnectedTo(int v){
        G.validateVertex(v);
        return dis[v] != Integer.MAX_VALUE;
    }

    public int distTo(int v){
        G.validateVertex(v);
        if(hasNegCycle) throw new RuntimeException("exist negative cycle.");
        return dis[v];
    }

    public Iterable<Integer> path(int t){//返回最短路径

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(!isConnectedTo(t)) return res;

        int cur = t;
        while(cur != s){
            res.add(cur);
            cur = pre[cur];
        }
        res.add(s);

        Collections.reverse(res);
        return res;
    }

    static public void main(String[] args){

        WeightedGraph g = new WeightedGraph("g.txt");
        BellmanFord bf = new BellmanFord(g, 0);
        if(!bf.hasNegativeCycle()){
            for(int v = 0; v < g.V(); v ++)
                System.out.print(bf.distTo(v) + " ");
            System.out.println();

            System.out.println(bf.path(3));
        }
        else
            System.out.println("exist negative cycle.");

        WeightedGraph g2 = new WeightedGraph("g2.txt");
        BellmanFord bf2 = new BellmanFord(g2, 0);
        if(!bf2.hasNegativeCycle()){
            for(int v = 0; v < g2.V(); v ++)
                System.out.print(bf2.distTo(v) + " ");
            System.out.println();
        }
        else
            System.out.println("exist negative cycle.");
    }
}

Floyed算法

求解所有点对最短路径--求解出图的直径--所有点对最短路径中的最大值

都是一个意思

v-b的最短路径+b-a的最短路径小于v-a的最短路径,跟新dis[v][a]

import java.util.Arrays;

public class Floyed {

    private WeightedGraph G;
    private int[][] dis;
    private boolean hasNegCycle = false;

    public Floyed(WeightedGraph G){

        this.G = G;
        //初始化过程
        dis = new int[G.V()][G.V()];
        for(int v = 0; v < G.V(); v ++)
            Arrays.fill(dis[v], Integer.MAX_VALUE);

        for(int v = 0; v < G.V(); v ++){//不是所有的距离都幅正无穷,如果v-w相连,赋dis为 边的权值
            dis[v][v] = 0;
            for(int w: G.adj(v))
                dis[v][w] = G.getWeight(v, w);
        }

        for(int t = 0; t < G.V(); t ++)//遍历中间寻找任意两点间的最短路径多经过的点
            for(int v = 0; v < G.V(); v ++)//遍历两点间最短路径的两个端点是谁
                for(int w = 0; w < G.V(); w ++)
                    if(dis[v][t] != Integer.MAX_VALUE && dis[t][w] != Integer.MAX_VALUE
                       && dis[v][t] + dis[t][w] < dis[v][w])//防止整型溢出
                        dis[v][w] = dis[v][t] + dis[t][w];
        //判断是否有负权环
        for(int v = 0; v < G.V(); v ++)
            if(dis[v][v] < 0)
                hasNegCycle = true;
    }

    public boolean hasNegativeCycle(){
        return hasNegCycle;
    }

    public boolean isConnectedTo(int v, int w){
        G.validateVertex(v);
        G.validateVertex(w);
        return dis[v][w] != Integer.MAX_VALUE;
    }

    public int distTo(int v, int w){//返回两点最短路径
        G.validateVertex(v);
        G.validateVertex(w);
        return dis[v][w];
    }

    static public void main(String[] args){

        WeightedGraph g = new WeightedGraph("g.txt");
        Floyed floyed = new Floyed(g);
        if(!floyed.hasNegativeCycle()){
            for(int v = 0; v < g.V(); v ++){
                for(int w = 0; w < g.V(); w ++)
                    System.out.print(floyed.distTo(v, w) + " ");
                System.out.println();
            }
        }
        else
            System.out.println("exist negative cycle.");

        WeightedGraph g2 = new WeightedGraph("g2.txt");
        Floyed floyed2 = new Floyed(g2);
        if(!floyed2.hasNegativeCycle()){
            for(int v = 0; v < g.V(); v ++){
                for(int w = 0; w < g.V(); w ++)
                    System.out.print(floyed2.distTo(v, w) + " ");
                System.out.println();
            }
        }
        else
            System.out.println("exist negative cycle.");
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值