Java 实现Dijkstra算法

今天看到一篇文章http://www.kidscode.cn/archives/3406
关于Dijkstra事迹的,就··研究一下最短路径算法,用java实现一下下,
基本照着 http://blog.51cto.com/ahalei/1387799 抄的··理解的也是囫囵吞枣。一会儿再去雕琢一下。先把代码放这儿

下面代码可以直接粘贴到IDE中执行,图矩阵已经写入代码


import java.util.ArrayList;
import java.util.Arrays;

public class DijkstraTest {
    public static void main(String[] args) {
        // 初始化数据
        /**
         * 邻接矩阵
         * 第一行两个整数 n m。
         * n 表示顶点个数(顶点编号为 1~n),m 表示边的条数。
         * 接下来 m 行表示,每行有 3 个数 x y z。表示顶点 x 到顶点 y 边的权值为 z。
         *  6 9
            1 2 1
            1 3 12
            2 3 9
            2 4 3
            3 5 5
            4 3 4
            4 5 13
            4 6 15
            5 6 4
         */
        String data = "6 9 1 2 1 1 3 12 2 3 9 2 4 3 3 5 5 4 3 4 4 5 13 4 6 15 5 6 4";
        ArrayList<Integer> datas = new ArrayList<Integer>();
        for(String s:data.split(" ")){
            datas.add(Integer.valueOf(s));
        }

        //读入n和m,n表示顶点个数,m表示边的条数
        int n = datas.get(0);
        int m = datas.get(1);
        int inf = 999999;
        int min = 999999;
        int[][] e = new int[n][n];
        // 先把矩阵的元素都设为 inf
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(i==j){
                    e[i][j] = 0;
                }else {
                    e[i][j] = inf;
                }
            }
        }
        for(int i=2;i<datas.size();i += 3){
            e[datas.get(i)-1][datas.get(i+1)-1] = datas.get(i+2);
        }
        // 打印出矩阵
        System.out.println("------------------邻接矩阵--------------------------");
        for(int i=0;i<n;i++){
            System.out.println(i+1+"   "+Arrays.toString(e[i]));
        }
        //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
        int[] dis = e[0];
        System.out.println("------------------初始dis数组--------------------------");
        System.out.println(Arrays.toString(dis));
        // book 数组初始化  1表示确定值,0表示估计值
        int[] book = new int[n];
        for(int i=0;i<n;i++){
            book[i] = 0;
        }
        book[0] = 1;

        //Dijkstra算法核心语句
        int u=0;
        for(int i=0;i<n-1;i++){
            min = inf;
            //找到估计值(book[j]==0)中  离1号顶点最近的顶点
            for(int j=0;j<n;j++){
                if(book[j]==0 && dis[j]<min){
                    min = dis[j];
                    u = j;
                }
            }
            // u 是最近点,下面以u为扩散点遍历
            book[u] = 1;
            for(int v=0;v<n;v++){
                if(dis[v] > dis[u]+e[u][v]){
                    dis[v] = dis[u]+e[u][v];
                }
            }
        }

        System.out.println("----------------------结果-------------------------------");
        System.out.println(Arrays.toString(dis));


    }


}

执行结果

------------------邻接矩阵--------------------------
1   [0, 1, 12, 999999, 999999, 999999]
2   [999999, 0, 9, 3, 999999, 999999]
3   [999999, 999999, 0, 999999, 5, 999999]
4   [999999, 999999, 4, 0, 13, 15]
5   [999999, 999999, 999999, 999999, 0, 4]
6   [999999, 999999, 999999, 999999, 999999, 0]
------------------初始dis数组--------------------------
[0, 1, 12, 999999, 999999, 999999]
----------------------结果-------------------------------
[0, 1, 8, 4, 13, 17]

本来以为想通了,但是仔细想想还是不通。
此代码得到结果了,我想本来想改改把图的过程打印出来的,没成功,还改着改着就··怀疑原来的想法了。

  • 为什么每次只需遍历估计值中最小的,就是最小路径?

8点了,从5点半坐在电脑前,没吃饭到现在。脑子不清晰了。下回分解吧··

8点半了··实在不甘心,似乎想通了上面的问题,但是不晓得怎么用语言描述。
而且··根据这个代码,打印出路径会很麻烦··懒得想了··回去洗澡··再说·

第二天早上还是放不下,又找了找,
发现一个可以打印出路径的例子 https://blog.csdn.net/qq_35644234/article/details/60870719,为啥我就想不出来呢···
这篇文章代码很工整,堪称模板,其实··我也想把dis改成类··但是时间紧迫,优化··再说吧,上代码


import java.util.ArrayList;
import java.util.Arrays;

public class DijkstraTest {
    public static void main(String[] args) {
        // 初始化数据
        /**
         * 邻接矩阵
         * 第一行两个整数 n m。
         * n 表示顶点个数(顶点编号为 1~n),m 表示边的条数。
         * 接下来 m 行表示,每行有 3 个数 x y z。表示顶点 x 到顶点 y 边的权值为 z。
         *  6 9
            1 2 1
            1 3 12
            2 3 9
            2 4 3
            3 5 5
            4 3 4
            4 5 13
            4 6 15
            5 6 4
         */

//      String data = "6 9 1 2 1 1 3 12 2 3 9 2 4 3 3 5 5 4 3 4 4 5 13 4 6 15 5 6 4";
        String data = "6 8 1 3 10 1 5 30 1 6 100 2 3 5 3 4 50 4 6 10 5 6 60 5 4 20";
        ArrayList<Integer> datas = new ArrayList<Integer>();
        for(String s:data.split(" ")){
            datas.add(Integer.valueOf(s));
        }

        //读入n和m,n表示顶点个数,m表示边的条数
        int n = datas.get(0);
        int m = datas.get(1);
        int inf = 999999;
        int min = 999999;
        int[][] e = new int[n][n];
        // 先把矩阵的元素都设为 inf
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(i==j){
                    e[i][j] = 0;
                }else {
                    e[i][j] = inf;
                }
            }
        }
        for(int i=2;i<datas.size();i += 3){
            e[datas.get(i)-1][datas.get(i+1)-1] = datas.get(i+2);
        }
        // 打印出矩阵
        System.out.println("------------------邻接矩阵--------------------------");
        for(int i=0;i<n;i++){
            System.out.println(i+1+"   "+Arrays.toString(e[i]));
        }

        // 以begin为起点
        int begin = 0;
        //初始化dis数组,这里是begin顶点到其余各个顶点的初始路程
        int[] dis = e[begin];
        System.out.println("------------------初始dis数组--------------------------");
        System.out.println(Arrays.toString(dis));
        // book 数组初始化  1表示确定值,0表示估计值
        int[] book = new int[n];
        // 记录最短路径
        String[] path = new String[n];
        for(int i=0;i<n;i++){
            book[i] = 0;
            path[i] = (begin+1)+" --> "+(i+1);
        }
        book[begin] = 1;



        //Dijkstra算法核心语句
        int u=0;
        for(int i=0;i<n-1;i++){
            min = inf;
            //找到估计值(book[j]==0)中  离1号顶点最近的顶点
            for(int j=0;j<n;j++){
                if(book[j]==0 && dis[j]<min){
                    min = dis[j];
                    u = j;
                }
            }
            // u 是最近点,下面以u为扩散点遍历
            book[u] = 1;
            for(int v=0;v<n;v++){
                //如果新得到的边可以影响其他为访问的顶点,那就就更新它的最短路径和长度
                if(e[u][v]<inf && dis[v] > dis[u]+e[u][v]){
                    dis[v] = dis[u]+e[u][v];
                    path[v] = path[u] + " --> "+(v+1);
                }
            }
        }

        System.out.println("---------------以"+(begin+1)+"为起点的图的最短路径为-------------------");
        System.out.println(Arrays.toString(dis));
        System.out.println("");
        for(int i=0;i<n;i++){
            System.out.println(path[i]+"  =  "+dis[i]);
        }


    }


}

运行结果

------------------邻接矩阵--------------------------
1   [0, 999999, 10, 999999, 30, 100]
2   [999999, 0, 5, 999999, 999999, 999999]
3   [999999, 999999, 0, 50, 999999, 999999]
4   [999999, 999999, 999999, 0, 999999, 10]
5   [999999, 999999, 999999, 20, 0, 60]
6   [999999, 999999, 999999, 999999, 999999, 0]
------------------初始dis数组--------------------------
[0, 999999, 10, 999999, 30, 100]
---------------以1为起点的图的最短路径为-------------------
[0, 999999, 10, 50, 30, 60]

1 --> 1  =  0
1 --> 2  =  999999
1 --> 3  =  10
1 --> 5 --> 4  =  50
1 --> 5  =  30
1 --> 5 --> 4 --> 6  =  60

调整了两个地方:

  • 可以打印最短路径
  • 可以自定义begin,以begin为起点查询最短路径

那么··又有问题了

  • 负权重的边怎么解决?
  • 此代码是有向图,无向图怎么办? 无向图的话那么邻接矩阵就是对称的,data反过来复制一份,有没有更好的解决办法?

下回分解哈

Dijkstra算法是用于解决单源最短路径问题的一种高效算法,特别是在有向无环图(DAG)或带权重的边的图中。在Java实现Dijkstra算法,你可以使用优先队列(通常使用`java.util.PriorityQueue`)来存储尚未确定最短路径的节点,以及一个哈希映射或邻接表来存储图的结构。 以下是Java实现Dijkstra算法的一个基本步骤: 1. 初始化:创建一个HashMap或类似的数据结构,将起点的距离设为0,其他所有节点的距离设为无穷大,同时标记为未访问。 2. 创建优先队列:将起点放入队列,并设置其优先级为起点距离。 3. 主循环:从队列中取出当前距离最小的节点(通常是最小优先级的节点),然后更新其相邻节点的距离,如果通过当前节点到达更短,就更新这些节点的距离并将它们加入队列。 4. 遍历邻接节点:对于每个相邻节点,检查通过当前节点到达它的路径是否比之前记录的更短。如果是,更新并标记该节点为已访问。 5. 重复步骤3和4,直到队列为空或找到终点。如果队列为空且未访问到终点,说明找不到从起点到终点的路径。 ```java import java.util.*; class Node implements Comparable<Node> { int id; int distance; Node previous; public Node(int id) { this.id = id; this.distance = Integer.MAX_VALUE; } @Override public int compareTo(Node other) { return Integer.compare(this.distance, other.distance); } } public class Dijkstra { // 使用PriorityQueue存储节点 private PriorityQueue<Node> queue = new PriorityQueue<>(); // 图的邻接表或哈希映射 private Map<Integer, List<Node>> graph; public List<Node> dijkstra(int start) { // ... (初始化、添加起点到队列等) while (!queue.isEmpty()) { Node current = queue.poll(); // 取出距离最小的节点 // 更新未访问的邻居 for (Node neighbor : graph.get(current.id)) { int distanceToNeighbor = current.distance + neighbor.distance; if (distanceToNeighbor < neighbor.distance) { neighbor.distance = distanceToNeighbor; neighbor.previous = current; queue.offer(neighbor); } } } // 返回从起点到终点的路径,如果找到 return buildPath(start); } private List<Node> buildPath(int end) { List<Node> path = new ArrayList<>(); Node currentNode = endNode(end); while (currentNode != null) { path.add(currentNode); currentNode = currentNode.previous; } Collections.reverse(path); return path; } // ... (获取结束节点的方法,可能需要一个额外的哈希映射存储每个节点的结束标识) } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值