图--加权有向图

最短路径

Dijkstra算法

本质为贪心算法,每次添加距离起点最近的非树顶点,适用于非负权重边

public class DikstraSP {
    private DirectedEdge[] edgeTo;
    private double[] distTo;
    private IndexMinPQ<Double> pq;

    public DikstraSP(EdgeWeightedDigraph g, int s){
        edgeTo = new DirectedEdge[g.V()];
        distTo = new double[g.V()];
        pq = new IndexMinPQ<Double>(g.V());

        for(int v=0; v<g.V(); v++)
            distTo[v] = Double.POSITIVE_INFINITY; //各顶点与起点距离初始化为无穷大
        distTo[s] = 0.0;

        pq.insert(s, 0.0);
        while(!pq.isEmpty())
            relax(g, pq.delMin()); //顶点的松弛
    }

    private void relax(EdgeWeightedDigraph g, int v){
        for(DirectedEdge e : g.adj(v)){
            int w = e.to();
            if(distTo[w]>distTo[v]+e.weight()){
                distTo[w] = distTo[v] +e.weight();
                edgeTo[w] = e;
                if(pq.contains(w)) pq.change(w, distTo[w]);
                else               pq.insert(w, distTo[w]);
            }
        }
    }

    public double distTo(int v){
        return distTo[v];
    }

    public boolean hasPathTo(int v){
        return distTo[v] < Double.POSITIVE_INFINITY;
    }

    public Iterable<DirectedEdge> pathTo(int v){
        if(!hasPathTo(v)) return null;
        Stack<DirectedEdge> path = new Stack<>();
        for(DirectedEdge e = edgeTo[v]; e!=null; e=edgeTo[e.from()])
            path.push(e);
        return path;
    }
}

无环加权有向图的最短路径

基于拓扑排序进行松弛

public class AcyclicSP {
    private DirectedEdge[] edgeTo;
    private double[] distTo;

    public AcyclicSP(EdgeWeightedDigraph g, int s){
        edgeTo = new DirectedEdge[g.V()];
        distTo = new double[g.V()];

        for(int v=0; v<g.V(); v++)
            distTo[v] = Double.POSITIVE_INFINITY;
        distTo[s] = 0.0;

        Topological top = new Topological(g);
        for(int v : top.order())        //拓扑顺序
            relax(g, v);
    }

    private void relax(EdgeWeightedDigraph g, int v){ //顶点松弛
        for(DirectedEdge e : g.adj(v)){
            int w = e.to();
            if(distTo[w]>distTo[v]+e.weight()){
                distTo[w] = distTo[v] +e.weight();
                edgeTo[w] = e;
            }
        }
    }

    public double distTo(int v){ //同上Dijkstra算法
    }

    public boolean hasPathTo(int v){
    }

    public Iterable<DirectedEdge> pathTo(int v){
    }
}

Bellman-Ford算法

单源最短路径,边权可正可负

  1. 初始化顶点到起点距离
  2. 循环松弛各顶点
  3. 检测是否有负权重环
public class BellmanFordSP {
    private double[] distTo;
    private DirectedEdge[] edgeTo;      //从起点到某个顶点的最后一条边
    private boolean[] onQ;              //该定点是否在队列中
    private Queue<Integer> queue;       //正在被放松的顶点
    private int cost;                   //relax()的调用次数
    private boolean flag ;              //有无负权重环

    public BellmanFordSP(EdgeWeightedDigraph g, int s){
        distTo = new double[g.V()];
        edgeTo = new DirectedEdge[g.V()];  
        onQ = new boolean[g.V()];
        queue = new Queue<>();
        for(int v=0; v<g.V(); v++)  
            distTo[v] = Double.POSITIVE_INFINITY; //初始化顶点到起点距离
        distTo[s] = 0.0;
        queue.enqueue(s);
        onQ[s] = true;
        while(!queue.isEmpty() && !hasNegativeCycle()){   //循环松弛各顶点
             int v = queue.dequeue();
             onQ[v] = false;
             relax(g, v);
         }
    }

    private void relax(EdgeWeightedDigraph g, int v){
        for(DirectedEdge e : g.adj(v)){
            int w = e.to();
            if(distTo[w]>distTo[v]+e.weight()){
                distTo[w] = distTo[v] +e.weight();
                edgeTo[w] = e;

                if(!onQ[w]){
                    queue.enqueue(w);
                    onQ[w] = true;
                }
            }
            if(cost++ % g.V() == 0)  //完全放松一轮后检测有无负权重环
                findNegativeCycle();
        }
    }

    private void findNegativeCycle(){
        flag = false; //判断是否含有负权回路  
        for(int i = 1; i <= edgeTo.length; i++)  
            if(distTo[edgeTo[i].from()] > distTo[edgeTo[i].to()] + edgeTo[i].weight())  
            {  
                flag = true;  
                break;  
            }  
    }

    private boolean hasNegativeCycle(){
        return flag;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值