算法复习(二)—— 最短路径

经典的Dijkstra最短路径算法只适用没有负边的情况。因为该算法,一个点到起点的最短距离一旦确定(采用贪心策略),它就不会再更改。而负边会破坏这个贪心的正确性。

对于有负边的情况,可以使用bellman-ford算法。它的思想是一条路径最多由n-1条边构成。因此,它从起始点出发,对图进行n-1次松弛操作。松弛操作就是借助一条边使得一个点到起点的距离变小。注意,不同于Dijkstra算法,一个点到起点的最短距离并不是一次确定的。原始的bellman-ford算法每一次松弛都遍历所有的边。这样是不必要的,因为只有一个被松弛过的点才可能有助于其它点的松弛。因此,有bellman-ford算法的各种优化版本,如大家熟知的SPFA算法。使用邻接边存储结构,SPFA的算法复杂度为O(k*e),据称通常k不大于2。因此,就算是求不含负边的最短路径问题,我们也可以使用SPFA。

在最短路径问题中,另一个经典算法就是求出所有点对之间的算法——Floyd算法。可以看到,Dijkstra, bellman-ford或SPFA根据一条边上的一个点松弛另一个点。不同地,Floyd采用动态规划的思想,状态转移方程可以参见该博文。其复杂度为O(n^3)


我分别写了两个graph类,MatrixGraph使用邻接矩阵存储结构,LinkedGraph使用邻接表结构。前者能很自然地实现Floyd算法,后者的实现只是调用了n次SPFA(理论上不会慢,但多次SPFA涉及到更多object,包括创建,垃圾回收等,所以实际很慢)

用于验证正确性的是hdu 1874

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
//1874 hdu

interface Graph {
    public void addEdge(int u, int v, float weight);
    public void spfa(int start);
    public float disBtw(int start, int dest);
    public void floyd();
}

// 邻接矩阵版
// 时间复杂度O(k*v*v), k一般不大于2
class MatrixGraph implements Graph{
    final static float MAXW = Float.MAX_VALUE;
    int eCnt;
    int nCnt;
    float con[][];
    boolean inQueue[];
    boolean bidir;
    int iqCnt[];
    float disMatrix[][];
    
    MatrixGraph() {        
    }
    
    MatrixGraph(int nCnt, boolean bidir) {
        this.nCnt = nCnt;
        this.bidir = bidir;
        nCnt++;
        con = new float[nCnt][nCnt];
        inQueue = new boolean[nCnt];
        iqCnt = new int[nCnt];
        disMatrix = new float[nCnt][];
        for(int i=0; i<nCnt; i++) {
            for(int j=0; j<nCnt; j++) {
                if(i != j ) {
                    con[i][j] = MAXW;
                } else {
                    con[i][j] = 0;
                }
                
            }
        }
        for(int i=0; i<nCnt; i++) {
            inQueue[i] = false;
            iqCnt[i] = 0;
            disMatrix[i]= null;
        }
    }
    
    public void addEdge(int u, int v, float weight) {
        if(weight < con[u][v]) {
            con[u][v] = weight;
            if(!bidir) {
                con[v][u] = weight;
            }
        }
    }
    
    public void spfa(int start) {
        float dis[] = new float[nCnt+1];
        for(int i = 0; i < nCnt; i++) {
            dis[i] = MAXW;
        }
        LinkedList<Integer> queue = new LinkedList<Integer>();
        dis[start] = 0;
        queue.push(start);
        iqCnt[start] = 1;
        while(!queue.isEmpty()) {
            int cur = queue.pop();
            inQueue[cur] = false;
            for(int i=0; i<nCnt; i++) {
                if(i!=cur && con[cur][i]!= MAXW) {
                    if(dis[i] > dis[cur]+con[cur][i]) {
                        dis[i] = dis[cur]+con[cur][i];
                        if(inQueue[i] == false) {
                            queue.push(i);
                            iqCnt[i]++;
                            inQueue[i] = true;
                        }
                    }
                }
            }
        }
        disMatrix[start] = dis;
    }
    
    public void floyd() {
        
        for (int i = 0; i < disMatrix.length; i++) {
            disMatrix[i] = new float[nCnt+1];
            System.arraycopy(con[i], 0, disMatrix[i], 0, con[i].length);
        }
        
        for(int k = 0; k < nCnt; k++)
        {
            for(int i = 0; i < nCnt; i++)
            {
                for(int j = 0; j < nCnt; j++)
                {
                    if(disMatrix[i][k] < MAXW && disMatrix[k][j] < MAXW)  {
                        if( disMatrix[i][j] > disMatrix[i][k] + disMatrix[k][j]) {
                            disMatrix[i][j] = disMatrix[i][k] + disMatrix[k][j];
                        }
                    }
                       
                }
            }
        }
    }
    
    public float disBtw(int start, int dest) {
        if(disMatrix[start]!=null && disMatrix[start][dest] != MAXW) {
            return disMatrix[start][dest];
        } else {
            return -1;
        }    
    }
}

//邻接边版 (一般比邻接矩阵快,因为图通常不太稠密)
//时间复杂度O(k*e), k一般不大于2
class LinkedGraph implements Graph{
    
    final static float MAXW = Float.MAX_VALUE;
    int eCnt;
    int nCnt;
    HashMap<Integer, Float> head[];
    boolean inQueue[];
    float disMatrix[][];
    boolean bidir;
    int iqCnt[];
    
    LinkedGraph() {
    }
    
    LinkedGraph(int nCnt, boolean bidir) {
        this.nCnt = nCnt;
        this.bidir = bidir;
        nCnt++;
        head = new HashMap[nCnt];
        inQueue = new boolean[nCnt];
        disMatrix = new float[nCnt][];
        iqCnt = new int[nCnt];
        for(int i=0; i<nCnt; i++) {
            head[i] = new HashMap<Integer, Float>();
        }
        for(int i=0; i<nCnt; i++) {
            inQueue[i] = false;
            iqCnt[i] = 0;
            disMatrix[i] = null;
        }
    }
    
    public void addEdge(int u, int v, float weight) {
        Float curW = head[u].get(v);
        if(curW == null) {
            head[u].put(v, weight);
            if(!bidir) {
                head[v].put(u, weight);
            }
        }
        else if(curW != null && curW>weight) {
            head[u].put(v, weight);
            if(!bidir) {
                head[v].put(u, weight);
            }
        }
    }
    
    public void spfa(int start) {
        LinkedList<Integer> queue = new LinkedList<Integer>();
        float[] dis = new float[nCnt+1];
        for(int i = 0; i < nCnt; i++) {
            dis[i] = MAXW;
        }
        dis[start] = 0;
        queue.push(start);
//        iqCnt[start] = 1;
        while(!queue.isEmpty()) {
            int cur = queue.pop();
            inQueue[cur] = false;
            for(Map.Entry<Integer,Float> entry : head[cur].entrySet()) {
                int nextNode = entry.getKey();
                float weight = entry.getValue();
                if(dis[nextNode] > dis[cur]+weight) {
                    dis[nextNode] = dis[cur]+weight;
                    if(inQueue[nextNode] == false) {
                        queue.push(nextNode);
//                        iqCnt[nextNode]++;
                        inQueue[nextNode] = true;
                    }
                }
            }
        }
        disMatrix[start] = dis;
    }
    
    // it may be very slow
    // as it involves one queue for every node in every graph
    // this is a fake-floyd implementation
    public void floyd() {
        for(int i = 0; i < nCnt; i++) {
            spfa(i);
        }
    }
    
    public float disBtw(int start, int dest) {
        if(disMatrix[start]!=null && disMatrix[start][dest] != MAXW) {
            return disMatrix[start][dest];
        } else {
            return -1;
        }    
    }
    
}

public class Main {

    public static void main(String[] args) throws IOException{
        int eCnt, nCnt;
        int u, v;
        float weight;
        int depa, dest;
        StreamTokenizer in = new StreamTokenizer(
                new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while(in.nextToken() != StreamTokenizer.TT_EOF) {
            nCnt = (int)in.nval;
            in.nextToken();
            eCnt = (int)in.nval;
//            MatrixGraph graph = new MatrixGraph(nCnt, false);    
            LinkedGraph graph = new LinkedGraph(nCnt, false);    
            for(int i=0; i<eCnt; i++) {
                in.nextToken();
                u = (int)in.nval;
                in.nextToken();
                v = (int)in.nval;
                in.nextToken();
                weight = (float)in.nval;
                graph.addEdge(u, v, weight);
            }
            in.nextToken();
            depa = (int)in.nval;
            in.nextToken();
            dest = (int)in.nval;
            
//            graph.spfa(depa);
            graph.floyd();
            out.println((int)graph.disBtw(depa, dest));
        }
        out.flush();
    }

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值