迪杰斯拉算法单源最短路径路径,将起点的邻边节点和距离先加入队列, 与先加入起点到队列的输出结果不一致

来源:12-4 Dijkstra 算法的优化

慕瓜7372772

2022-03-29

图片描述
这个是我在牛客网刷题的时候遇到的问题

迪杰斯拉算法单源最短路径路径,将起点的邻边节点和距离先加入队列 计算结果是1370, 与先加入起点到队列的输出结果1205不一致

测试用例

81 ,167,[[32,72,724],[63,68,733],[21,36,130],[47,55,445],[14,31,750],[42,78,963],[29,37,209],[4,59,908],[8,13,920],[7,76,855],[4,12,56],[4,67,75],[26,33,741],[58,61,779],[49,59,667],[57,69,118],[21,47,119],[4,46,91],[35,64,60],[32,46,432],[31,45,875],[44,76,74],[53,61,476],[31,72,329],[10,52,72],[28,40,787],[63,81,893],[54,57,681],[33,65,176],[12,76,139],[67,69,83],[32,69,621],[24,32,722],[25,43,581],[19,69,891],[49,69,349],[30,41,502],[32,77,346],[14,16,413],[56,61,690],[26,41,126],[33,52,382],[17,55,705],[24,72,722],[39,49,360],[40,45,811],[58,72,425],[57,68,542],[2,23,502],[28,53,23],[19,55,48],[32,73,636],[48,63,456],[10,30,962],[9,38,556],[68,80,44],[55,74,388],[43,80,319],[34,36,383],[9,75,642],[76,80,954],[10,22,257],[3,62,490],[53,60,233],[31,53,611],[37,45,641],[12,13,352],[20,75,302],[37,41,2],[17,49,895],[27,61,361],[1,57,699],[10,23,42],[11,47,847],[18,65,380],[61,81,710],[33,55,73],[46,67,703],[54,77,154],[44,47,74],[5,34,244],[9,72,118],[9,39,552],[3,19,731],[25,60,869],[50,62,103],[3,56,53],[3,24,88],[25,77,850],[13,32,911],[16,63,580],[7,77,876],[37,63,615],[69,74,179],[10,46,883],[30,43,262],[7,62,22],[3,40,686],[30,53,717],[10,30,267],[5,66,389],[24,48,81],[52,60,28],[2,10,152],[1,57,864],[9,65,428],[23,36,610],[15,49,236],[61,70,840],[13,65,893],[25,32,993],[28,47,461],[29,61,307],[19,48,423],[32,62,640],[30,59,883],[10,61,245],[9,56,765],[74,81,209],[40,68,250],[77,79,175],[45,58,271],[55,76,504],[32,33,700],[32,45,118],[8,44,828],[40,79,398],[35,45,935],[33,78,567],[4,26,161],[9,52,670],[12,46,555],[10,51,760],[2,70,433],[18,68,726],[38,53,759],[12,26,273],[1,75,933],[6,13,690],[6,24,668],[47,66,989],[21,73,647],[53,71,669],[3,15,449],[3,57,123],[31,67,154],[20,61,385],[44,73,393],[19,59,953],[15,36,684],[13,69,48],[70,80,811],[51,53,427],[11,66,599],[32,68,506],[13,56,858],[27,70,447],[12,41,364],[27,75,89],[16,74,412],[16,37,676],[43,49,986],[50,75,187],[26,77,390],[57,66,755],[22,42,502],[2,59,424]]

完整代码如下

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int 顶点数
     * @param m int 边数
     * @param graph int二维数组 一维3个数据,表示顶点到另外一个顶点的边长度是多少​
     * @return int
     */
    private HashSet<Edge>[] edges;
    class Edge{
        int w, weight;
        public Edge(int w, int weight){
            this.w = w;
            this.weight = weight;
        }
    }
    class Node implements Comparable<Node> {
        int w, dis;
        public Node(int w, int dis){
            this.w = w;
            this.dis = dis;
        }
        @Override
        public int compareTo(Node other) {
            return dis - other.dis;
        }
    }
    private boolean[] visited;
    private int[] dis;
    private int[] pre;
    public int findShortestPath (int n, int m, int[][] graph) {
        // djistrak算法
        edges = new HashSet[n+1];
        for(int i=1; i<=n; i++) {
            edges[i] = new HashSet<Edge>();
        }
        visited = new boolean[n+1];
        dis = new int[n+1];
        pre = new int[n+1];
        Arrays.fill(dis, -1);
        // 添加节点和边,权重
        for(int i=0; i<m; i++) {
            int v = graph[i][0];
            int w = graph[i][1];
            int weight = graph[i][2];
            edges[v].add(new Edge(w, weight));
        }
        // print();
        
        // 1到n的最短路径
        dis[1] = 0;
        pre[1] = 1;
        PriorityQueue<Node> queue = new PriorityQueue<>();
//         for(Edge e : edges[1]) {
//             // 将1的边加入到优先队列中
//             dis[e.w] = e.weight;
//             queue.add(new Node(e.w, dis[e.w]));
//             pre[e.w] = 1;
//         }
        queue.add(new Node(1, 0));
//         visited[1] = true;
        while(!queue.isEmpty()) {
            Node cur = queue.poll();
            if (visited[cur.w]) continue;
            visited[cur.w] = true;
            for(Edge e : edges[cur.w]) {
                if (!visited[e.w]) {
                    // 从原点到w路径大于从cur.w过来
                    if (dis[cur.w] != -1 && (dis[e.w] == -1 || dis[e.w] > dis[cur.w] + e.weight))  {
                        dis[e.w] = dis[cur.w] + e.weight;
                        pre[e.w] = cur.w;
                        queue.add(new Node(e.w, dis[e.w]));
                    }
                }
            }
        }
        // 1205
        return dis[n];
    }
    
    private void print(int n){
        for(int i=1; i<=n; i++) {
            System.out.print("v:" + i + " ==> ");
            for(Edge e : edges[i]) {
                System.out.print(e.w + ":" + e.weight + "  ");
            }
            System.out.println();
        }
    }
}
写回答

1回答

liuyubobobo

2022-03-29

我没有理解你的问题。什么叫“你那边结果不对?”你的意思是“我这边的结果是对的?你得到的结果和课程中的某些地方矛盾吗?”如果是这样的话,请指出和课程的视频的哪里有冲突,给我一个视频位置,或者课程代码的链接,我来看一下?


课程代码的写法是只添加一个起始点:https://git.imooc.com/coding-370/Play-with-Graph-Theory-Algorithm/src/master/12-Shortest-Path/04-Dijkstra-Algorithm-Optimized/src/Dijkstra.java


如果你只是觉得你的逻辑应该没有问题,可实际却出现了问题,请给我你测试的平台的链接和能复现你错误的代码,我来简单看一下。


==========


测试用例中有重边,导致在外面添加边的时候,dis 的赋值,可能一个更长的距离覆盖了更短的距离。


以下代码可以 ac。改动了三行,相应的地方注释里有 !!! 的标识。


import java.util.*;

public class Solution {
    private HashSet<Edge>[] edges;
    class Edge{
        int w, weight;
        public Edge(int w, int weight){
            this.w = w;
            this.weight = weight;
        }
    }
    class Node implements Comparable<Node> {
        int w, dis;
        public Node(int w, int dis){
            this.w = w;
            this.dis = dis;
        }
        @Override
        public int compareTo(Node other) {
            return dis - other.dis;
        }
    }
    private boolean[] visited;
    private int[] dis;
    private int[] pre;
    public int findShortestPath (int n, int m, int[][] graph) {
        // djistrak算法
        edges = new HashSet[n+1];
        for(int i=1; i<=n; i++) {
            edges[i] = new HashSet<Edge>();
        }
        visited = new boolean[n+1];
        dis = new int[n+1];
        pre = new int[n+1];
        Arrays.fill(dis, Integer.MAX_VALUE); // !!!这里初始化成 max_value 更方便
        // 添加节点和边,权重
        for(int i=0; i<m; i++) {
            int v = graph[i][0];
            int w = graph[i][1];
            int weight = graph[i][2];
            edges[v].add(new Edge(w, weight));
        }
        // print();
        
        // 1到n的最短路径
        dis[1] = 0;
        pre[1] = 1;
        PriorityQueue<Node> queue = new PriorityQueue<>();
        for(Edge e : edges[1]) {
            // 将1的边加入到优先队列中
            dis[e.w] = Math.min(dis[e.w], e.weight); // !!!因为重边,这里要取一个 min
            queue.add(new Node(e.w, dis[e.w]));
            pre[e.w] = 1;
        }
        visited[1] = true;
        while(!queue.isEmpty()) {
            Node cur = queue.poll();
            if (visited[cur.w]) continue;
            visited[cur.w] = true;
            for(Edge e : edges[cur.w]) {
                if (!visited[e.w]) {
                    // 从原点到w路径大于从cur.w过来
                    if (dis[e.w] > dis[cur.w] + e.weight)  { // !!! 这个 if 我做了化简
                        dis[e.w] = dis[cur.w] + e.weight;
                        pre[e.w] = cur.w;
                        queue.add(new Node(e.w, dis[e.w]));
                    }
                }
            }
        }
        return dis[n];
    }
}


继续加油!:)

0
2
liuyubobobo
回复
慕瓜7372772
我添加到了原回答中。继续加油!:)
2022-03-29
共2条回复

玩转算法系列--图论精讲(Java版)

30+小时系统学习,bobo带你克服被图论支配的恐惧

1591 学习 · 324 问题

查看课程