Dijkstra's - HolmesJJ/Data-Structures-and-Algorithms GitHub Wiki

入门链接

【算法】最短路径查找—Dijkstra算法 Dijkstra(迪杰斯特拉)算法理解 图论-轻松上手-Dijkstra(迪杰斯特拉)算法演示 图论最短距离(Shortest Path)算法动画演示-Dijkstra(迪杰斯特拉)和Floyd(弗洛伊德)

详解

【数据结构】图(最短路径Dijkstra算法)的JAVA代码实现

使用条件

  • 所有的边权 >= 0
  • 解释:Dijkstra算法可以很好地解决无负权图的最短路径问题,但如果出现了负权边,Dijkstra算法就会失效。例如下图中,若A为源点,首先会将点B和点C的dis值变为-1和1,接着由于点B的dist值最小,因此用点B去更新其未访问的邻接点(虽然并没有)。在这之后点B标记为已访问,于是将无法被从点C出发的边CB更新,因此最后dist(B)就是-1,但显然A到B的最短路径应该是A -> C -> B,即-4。

标准写法

public class Dijkstra<T> {

    // 源点的下标
    private int srcIndex;
    // 当前图
    private AdjacencyList<T> adjList;
    // 标记结点是否已经被访问
    private boolean[] visited;
    // 结点到源点的最短距离
    private int[] distTo;
    // 在已选集合中的结点的父结点
    private int[] parent;
    // 构造优先队列
    PriorityQueue<DijkstraNode> pq;

    public Dijkstra(int srcIndex, AdjacencyList<T> adjList) {
        this.srcIndex = srcIndex;
        this.adjList = adjList;
        this.visited = new boolean[adjList.size()];
        Arrays.fill(visited, false);
        this.distTo = new int[adjList.size()];
        // 初始化结点到源点的最短距离为无穷大
        Arrays.fill(distTo, Integer.MAX_VALUE);
        this.parent = new int[adjList.size()];
        // 初始化父结点都是-1
        Arrays.fill(parent, -1);
        pq = new PriorityQueue<>();
    }

    public void dijkstra() {
        if (adjList == null || adjList.getNodes().size() == 0) {
            return;
        }
        // 源点到源点自己的距离为0
        distTo[srcIndex] = 0;
        // 源点被访问
        visited[srcIndex] = true;
        // 源点的父结点就是自己
        parent[srcIndex] = srcIndex;
        // 从源点开始
        pq.add(new DijkstraNode(srcIndex, 0));
        while (!pq.isEmpty()) {
            // 获取当前优先队列中结点到源点距离最短的结点
            DijkstraNode node = pq.poll();
            if (node == null) {
                return;
            }
            // 结点被访问
            visited[node.getIndex()] = true;
            // 把当前与结点的相邻的结点都加入到优先队列
            ArrayList<AdjListNode> list = adjList.getNodes().get(node.getIndex()).getRoot();
            for (int i = 0; i < list.size(); i++) {
                // 排除已经访问过的结点
                // 更新结点到源点距离的最短距离 或 直接加入到优先队列
                if (!visited[list.get(i).getIndex()] && distTo[list.get(i).getIndex()] > list.get(i).getWeight()) {
                    DijkstraNode newNode = new DijkstraNode(list.get(i).getIndex(), list.get(i).getWeight());
                    pq.remove(newNode);
                    pq.add(newNode);
                    // 更新最短距离
                    distTo[list.get(i).getIndex()] = list.get(i).getWeight();
                    // 更新父结点
                    parent[list.get(i).getIndex()] = node.getIndex();
                }
            }
        }
    }

    public void print(int index) {
        if (index >= parent.length || parent[index] <= 0) {
            System.out.print("无最短路径!");
            return;
        }
        String result = " -> " + adjList.getNodes().get(index).getData().toString();
        while (parent[index] > 0) {
            result = " -> " + adjList.getNodes().get(parent[index]).getData() + result;
            index = parent[index];
        }
        result = adjList.getNodes().get(parent[index]).getData() + result;
        System.out.println(result);
    }
}

性能比较

PQ Implementation insert deleteMin decreaseKey Total
Array 1 V 1 1
AVL Tree logV logV logV O(ElogV)
d-way Heap dlog(d)V dlog(d)V log(d)V O(Elog(E/V)V)
Fibonacci Heap 1 logV 1 O(E + VlogV))

要点

  • 维护一个集合,每次需要找到最小的距离,优先队列或AVL树搜索的时间复杂度是0(logn)
  • E条边,V个结点,时间复杂度是