用邻接表和最小堆实现Dijkstra 最短路算法 (Java实现)

 

  • 算法特点:

    迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

  • 算法的思路

    Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。 
    然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点, 
    然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。 
    然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。

      对于邻接表不太熟悉的可以看下面这个链接,邻接表

      下面直接简单粗暴贴代码

import java.util.Comparator;
import java.util.PriorityQueue;

/*
使用邻接表和优先队列(小根堆)的D算法
顶点从1开始
*/
public class Dijkstra {
    int n;//点数
    int m;//边数
    //邻接表
    private int[] U; //第i条边的起点
    private int[] V; //第i条边的终点
    private int[] W; //第i条边的长度
    private int[] first; //起点为i的第一条边
    private int[] next; //起点为i的下一条边,-1表示没了


    class distanceTo {
        int des;
        int distance;

        distanceTo(int des, int distance) {
            this.des = des;
            this.distance = distance;
        }
    }

    Dijkstra(int n, int m, int[][] edges) { //点数,边数,输入数组(起点,终点,长度)
        if (m != edges.length)
            throw new IllegalArgumentException();
        U = new int[m + 1];
        V = new int[m + 1];
        W = new int[m + 1];
        first = new int[n + 1];
        next = new int[n + 1];
        for (int i = 0; i < edges.length; ++i) { //读入每一条边
            if (edges[i].length != 3)
                throw new IllegalArgumentException();
            U[i + 1] = edges[i][0];
            V[i + 1] = edges[i][1];
            W[i + 1] = edges[i][2];
        }
        for (int i = 1; i < first.length; ++i) {
            first[i] = -1;
        }
        for (int i = 1; i <= m; ++i) {
            next[i] = first[U[i]]; //邻接表的关键
            first[U[i]] = i;
        }
    }

    public int[] findNearestPath(int s) { //源点i到其他点的最短路
        PriorityQueue<distanceTo> updatingDistance = new PriorityQueue<>(new Comparator<distanceTo>() {
            @Override
            public int compare(distanceTo o1, distanceTo o2) {
                return o1.distance - o2.distance;
            }
        });
        int[] distance = new int[n + 1];
        for (int i = 1; i < first.length; ++i) {
            if (i == s)
                continue;
            distance[i] = Integer.MAX_VALUE;
        }
        int[] path = new int[n + 1];

        int i = first[s];
        if (i == -1)
            return path;
        while (i != -1) { //遍历s为源点的所有边
            distance[V[i]] = W[i];
            updatingDistance.offer(new distanceTo(V[i], W[i]));
            i = next[i];
        }
        for (i = 1; i <= n; i++) { //因为邻接表不会存不能直达的点
            if (distance[i] == Integer.MAX_VALUE)
                updatingDistance.offer(new distanceTo(i, distance[i]));
        }
        for (i = 1; i <= n - 1; ++i) { //D算法要循环n-1次
            int t = first[updatingDistance.poll().des]; //当前到点w距离最短

            while (t != -1) {
                if (distance[V[i]] > distance[U[i]] + W[i]) {
                    distance[V[i]] = distance[U[i]] + W[i];
                    updatingDistance.offer(new distanceTo(V[i], distance[V[i]]));//不需要删掉被覆盖的,因为他们会被排在后面
                    path[V[i]] = U[i];
                }
            }
        }
        return path;
    }
}

最后返回的path数组记录了最短的路径,比如path[5] = 3,表示到点5的最短路径上一步是在点3,一直到某个值等于出发点,即得到完整路径。
 

阅读更多
换一批

没有更多推荐了,返回首页