Java实现Dijkstra算法

本文介绍了一种使用Java实现Dijkstra算法的方法,通过定义Node、Edge、Graph和Dijkstra类来构造图并找到从指定起点到其他各点的最短路径。

Dijkstra算法:用于计算图中某一点到其他各点的最短路径。关于Dijkstra算法的说明可以参考 数据结构相关书籍。

为Dijkstra算法设计的类:
1. Node        节点类

2. Edge        边类
3. Graph       图类
4. Dijkstra     Dijkstra算法类
---------------------------------------------------------------------------------------------------------------------------------
Node类:
package com.sabrina.dijkstra;

import java.util.ArrayList;
import java.util.List;

public class Node {

    // 节点编号
    private String id;
    // 从当前节点出发的边的信息列表
    private List outEdges;
   
    public Node(String id) {
        this.id = id;
        outEdges = new ArrayList();
    }
   
    public void addOutEdge(Edge edge) {
        if(edge != null)
            outEdges.add(edge);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public List getOutEdges() {
        return outEdges;
    }

    public void setOutEdges(List outEdges) {
        this.outEdges = outEdges;
    }
}
 
Edge类:
package com.sabrina.dijkstra;

public class Edge {

    // 边的起始节点编号
    private String startNodeID;
    // 边的末尾节点编号
    private String endNodeID;
    // 边的权值
    private double weight;
   
    public String getStartNodeID() {
        return startNodeID;
    }
    public void setStartNodeID(String startNodeID) {
        this.startNodeID = startNodeID;
    }
    public String getEndNodeID() {
        return endNodeID;
    }
    public void setEndNodeID(String endNodeID) {
        this.endNodeID = endNodeID;
    }
    public double getWeight() {
        return weight;
    }
    public void setWeight(double weight) {
        this.weight = weight;
    }
}
 
Graph类:


 
package com.sabrina.dijkstra;

import java.util.ArrayList;
import java.util.List;

public class Graph {

    // 图中的节点列表
    public List nodeList = null;

    public Graph() {
        nodeList = new ArrayList();
    }

    public List getNodeList() {
        return nodeList;
    }

    // initialize
    public void init() {
        // ************************ Node A ***********************
        Node aNode = new Node("A");
        nodeList.add(aNode);
        // A -> B
        Edge a2bEdge = new Edge();
        a2bEdge.setStartNodeID(aNode.getId());
        a2bEdge.setEndNodeID("B");
        a2bEdge.setWeight(10);
        aNode.addOutEdge(a2bEdge);
        // A -> C
        Edge a2cEdge = new Edge();
        a2cEdge.setStartNodeID(aNode.getId());
        a2cEdge.setEndNodeID("C");
        a2cEdge.setWeight(20);
        aNode.addOutEdge(a2cEdge);
        // A -> E
        Edge a2eEdge = new Edge();
        a2eEdge.setStartNodeID(aNode.getId());
        a2eEdge.setEndNodeID("E");
        a2eEdge.setWeight(30);
        aNode.addOutEdge(a2eEdge);

        // ************************ Node B ***********************
        Node bNode = new Node("B");
        nodeList.add(bNode);
        // B -> C
        Edge b2cEdge = new Edge();
        b2cEdge.setStartNodeID(bNode.getId());
        b2cEdge.setEndNodeID("C");
        b2cEdge.setWeight(5);
        bNode.addOutEdge(b2cEdge);
        // B -> E
        Edge b2eEdge = new Edge();
        b2eEdge.setStartNodeID(bNode.getId());
        b2eEdge.setEndNodeID("E");
        b2eEdge.setWeight(10);
        bNode.addOutEdge(b2eEdge);

        // ************************ Node C ***********************
        Node cNode = new Node("C");
        nodeList.add(cNode);
        // C -> D
        Edge c2dEdge = new Edge();
        c2dEdge.setStartNodeID(cNode.getId());
        c2dEdge.setEndNodeID("D");
        c2dEdge.setWeight(30);
        cNode.addOutEdge(c2dEdge);
       
        // ************************ Node D ***********************
        Node dNode = new Node("D");
        nodeList.add(dNode);
       
        // ************************ Node E ***********************
        Node eNode = new Node("E");
        nodeList.add(eNode);
        // E -> D
        Edge e2dEdge = new Edge();
        e2dEdge.setStartNodeID(eNode.getId());
        e2dEdge.setEndNodeID("D");
        e2dEdge.setWeight(20);
        eNode.addOutEdge(e2dEdge);
       
    }
}
 
Dijkstra类:
package com.sabrina.dijkstra;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class Dijkstra {

    // 起始节点编号
    private String startNodeID;
    // 未被处理的节点集合
    private List sourceNodeIDList = null;
    // 已被处理的节点集合
    private List desNodeIDList = null;

    // '节点编号' 和  '起始节点与当前节点之间的最短路径' 的映射关系
    private Map nodeidShortestRouteMapping = null;
    // '节点编号' 和  '起始节点到当前节点之间的最短路径 的 上一跳节点编号' 的映射关系
    private Map nodeidLastNodeidMapping = null;
    // '节点编号' 和  '节点对象'的 映射关系
    private Map idNodeMapping = null;
   
    public Dijkstra(Graph graph, String startNodeID) {
        this.startNodeID = startNodeID;
       
        // 初始化
        sourceNodeIDList = new ArrayList();
        desNodeIDList = new ArrayList();
        nodeidShortestRouteMapping = new HashMap();
        nodeidLastNodeidMapping = new HashMap();
        idNodeMapping = new HashMap();
       
        for(Node node : graph.getNodeList()) {
            if(node.getId().equals(startNodeID)) {
                desNodeIDList.add(startNodeID);
                // 起始节点到起始节点的最短路径长度为0
                nodeidShortestRouteMapping.put(startNodeID, 0d);
            }
            else {
                sourceNodeIDList.add(node.getId());
                // 非起始节点到起始节点的最短路径长度为 '无穷大'
                nodeidShortestRouteMapping.put(node.getId(), Double.MAX_VALUE);
            }
            // 设置到节点最短路径的上一跳节点为'null'
            nodeidLastNodeidMapping.put(node.getId(), null);
            idNodeMapping.put(node.getId(), node);
        }
    }
   
    public void start() {
        Node nextNode = null;
        Node currentNode = null;
        String nextNodeID = "";
        do {
            if(nextNode == null) {
                currentNode = idNodeMapping.get(startNodeID);
            }
            else {
                currentNode = nextNode;
            }
            nextNodeID = getNextNode(currentNode);
           
            nextNode = idNodeMapping.get(nextNodeID);
            System.out.println("nextNode.id:" + nextNode.getId());
           
            sourceNodeIDList.remove(nextNode.getId());
            System.out.println("sourceNodeIDList:" + sourceNodeIDList.toString());
        } while(sourceNodeIDList.size() > 0);
    }
   
   
    public String getNextNode(Node currentNode) {
        System.out.println("============= currentNode.id: " + currentNode.getId() + " =============");
        double shortestPath = Double.MAX_VALUE;
        String nextNodeID = "";
        //  遍历从当前节点出发的邻接节点
        for(Edge nextEdge : currentNode.getOutEdges()) {
            System.out.println("nextEdge.endNodeid:" + nextEdge.getEndNodeID());
            // 判断 '经过当前节点至邻接节点的距离' < '之前记录的从源节点出发到邻接节点的距离'
            if((nextEdge.getWeight() + nodeidShortestRouteMapping.get(currentNode.getId()))
                    < nodeidShortestRouteMapping.get(nextEdge.getEndNodeID())) {
                // 更新路由表
                nodeidShortestRouteMapping.put(nextEdge.getEndNodeID(),
                        nextEdge.getWeight() + nodeidShortestRouteMapping.get(currentNode.getId()));
                nodeidLastNodeidMapping.put(nextEdge.getEndNodeID(),
                        currentNode.getId());
            }
        }
        // 从未被处理过的节点集合中,取出权值最小的节点
        for(String nodeID : sourceNodeIDList) {
            if(nodeidShortestRouteMapping.get(nodeID) < shortestPath) {
                shortestPath = nodeidShortestRouteMapping.get(nodeID);
                nextNodeID = nodeID;
            }
        }
        if(sourceNodeIDList.size() == 1) // 从未被处理过的节点集合中,取出最后一个节点
            return sourceNodeIDList.get(0);
        return nextNodeID;
    }
   
   
    public Map getNodeidShortestRouteMapping() {
        return nodeidShortestRouteMapping;
    }

    public Map getNodeidLastNodeidMapping() {
        return nodeidLastNodeidMapping;
    }

    public static void main(String[] args) {
        Graph graph = new Graph();
        graph.init();
       
        Dijkstra dijkstra = new Dijkstra(graph, "A");
        dijkstra.start();

        // 打印最终的路由表
        Iterator it = dijkstra.getNodeidShortestRouteMapping().keySet().iterator();
        while(it.hasNext()) {
            String id = it.next();
            System.out.println("nodeId: "  + id + ", shortest length: " + dijkstra.getNodeidShortestRouteMapping().get(id)
                    + ", last nodeId: " + dijkstra.getNodeidLastNodeidMapping().get(id));
        }
    }
}
 
最终执行结果
============= currentNode.id: A =============
nextEdge.endNodeid:B
nextEdge.endNodeid:C
nextEdge.endNodeid:E
nextNode.id:B
sourceNodeIDList:[C, D, E]
============= currentNode.id: B =============
nextEdge.endNodeid:C
nextEdge.endNodeid:E
nextNode.id:C
sourceNodeIDList:[D, E]
============= currentNode.id: C =============
nextEdge.endNodeid:D
nextNode.id:E
sourceNodeIDList:[D]
============= currentNode.id: E =============
nextEdge.endNodeid:D
nextNode.id:D
sourceNodeIDList:[]
nodeId: D, shortest length: 40.0, last nodeId: E
nodeId: E, shortest length: 20.0, last nodeId: B
nodeId: A, shortest length: 0.0, last nodeId: null
nodeId: B, shortest length: 10.0, last nodeId: A
nodeId: C, shortest length: 15.0, last nodeId: B
PS:如有写的不对的,敬请纠正~
Dijkstra算法是一种经典的贪心算法,用于解决单源最短路径问题。其核心思想是通过逐步扩展最短路径集合,找到图中从起点到其他所有节点的最短路径。该算法适用于边权值为非负数的图结构,能够高效地求解最短路径问题[^1]。 以下是一个使用Java实现Dijkstra算法的示例代码,采用邻接表表示图,并使用优先队列优化选取当前距离最小的节点: ```java import java.util.*; class Graph { private int V; // 顶点数量 private List<List<Node>> adjList; static class Node { int vertex, weight; Node(int vertex, int weight) { this.vertex = vertex; this.weight = weight; } } Graph(int V) { this.V = V; adjList = new ArrayList<>(); for (int i = 0; i < V; i++) { adjList.add(new ArrayList<>()); } } void addEdge(int u, int v, int w) { adjList.get(u).add(new Node(v, w)); adjList.get(v).add(new Node(u, w)); // 如果是无向图 } void dijkstra(int start) { int[] dist = new int[V]; Arrays.fill(dist, Integer.MAX_VALUE); dist[start] = 0; PriorityQueue<Node> pq = new PriorityQueue<>((a, b) -> a.weight - b.weight); pq.add(new Node(start, 0)); while (!pq.isEmpty()) { Node current = pq.poll(); int u = current.vertex; int currentDist = current.weight; if (currentDist > dist[u]) continue; for (Node neighbor : adjList.get(u)) { int v = neighbor.vertex; int weight = neighbor.weight; if (dist[v] > dist[u] + weight) { dist[v] = dist[u] + weight; pq.add(new Node(v, dist[v])); } } } System.out.println("最短路径距离:"); for (int i = 0; i < V; i++) { System.out.println("顶点 " + start + " 到顶点 " + i + " 的最短距离为 " + dist[i]); } } } public class DijkstraExample { public static void main(String[] args) { Graph graph = new Graph(6); graph.addEdge(0, 1, 2); graph.addEdge(0, 2, 4); graph.addEdge(1, 2, 1); graph.addEdge(1, 3, 7); graph.addEdge(2, 4, 3); graph.addEdge(3, 4, 2); graph.addEdge(3, 5, 6); graph.addEdge(4, 5, 5); graph.dijkstra(0); } } ``` 上述代码中,`Graph`类用于表示图结构,`dijkstra`方法实现Dijkstra算法的核心逻辑。通过优先队列(`PriorityQueue`)来维护当前未处理节点中距离最小的节点,确保每次扩展的路径都是当前最短的。该算法的时间复杂度为 $O(E \log V)$,其中 $E$ 是边的数量,$V$ 是顶点数量,适用于稀疏图[^3]。 需要注意的是,由于Dijkstra算法依赖于每次选取当前最短路径的节点并将其标记为已处理,因此在存在负权边的情况下,该算法可能会产生错误的结果。负权边可能导致某些节点的最短路径在算法执行后期被进一步优化,但由于这些节点已经被处理,它们的距离值不会被重新计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值