- 关于 Dijkstra 算法的介绍和面向过程的代码实现见 Dijkstra 算法:思路详解 & 代码实现 这篇博文。
下面介绍使用面向对象的实现方法。
基础类型:
顶点:
public class Vertex {
private String name;
public Vertex(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
有向边:
public class DirectedEdge {
private Vertex startV;
private Vertex endV;
private int weight;
public DirectedEdge(Vertex startV, Vertex endV, int weight) {
this.startV = startV;
this.endV = endV;
this.weight = weight;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Vertex getStartV() {
return startV;
}
public void setStartV(Vertex startV) {
this.startV = startV;
}
public Vertex getEndV() {
return endV;
}
public void setEndV(Vertex endV) {
this.endV = endV;
}
}
图:
import java.util.ArrayList;
import java.util.List;
public class Graph {
private List<Vertex> vertexList = new ArrayList<>();
private List<DirectedEdge> directedEdgeList = new ArrayList<>();
public void addDirectedE(DirectedEdge e) {
directedEdgeList.add(e);
// 添加边时会将新的顶点加入 vertexList 中
if (!vertexList.contains(e.getStartV())) {
vertexList.add(e.getStartV());
} else if (!vertexList.contains(e.getEndV())) {
vertexList.add(e.getEndV());
}
}
public List<DirectedEdge> getDirectedEdgeList() {
return directedEdgeList;
}
public void setDirectedEdgeList(List<DirectedEdge> mDirectedEdgeList) {
this.directedEdgeList = mDirectedEdgeList;
}
public List<Vertex> getVertexList() {
return vertexList;
}
public void setVertexList(List<Vertex> mVertexList) {
this.vertexList = mVertexList;
}
}
路径:
import java.util.ArrayList;
import java.util.List;
public class Path {
private List<DirectedEdge> directedEdgeList = new ArrayList<>();
public Path(List<DirectedEdge> directedEdgeList) {
this.directedEdgeList.addAll(directedEdgeList);
}
public Path(DirectedEdge e) {
this.directedEdgeList.add(e);
}
public void addPath(DirectedEdge e) {
directedEdgeList.add(e);
}
/*
* 计算 directedEdgeList 中所有边权重的总和
* */
public int sumWeight() {
int sumWeight = 0;
for (int i = 0; i < directedEdgeList.size(); i++) {
sumWeight += directedEdgeList.get(i).getWeight();
}
return sumWeight;
}
public List<DirectedEdge> getDirectedEdgeList() {
return directedEdgeList;
}
public void setDirectedEdgeList(List<DirectedEdge> mDirectedEdgeList) {
this.directedEdgeList = mDirectedEdgeList;
}
/*
* Path 的描述:将 directedEdgeList 中边的顶点通过 "v -> v" 的形式描述出来
* */
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
DirectedEdge tmpE = null;
for (int i = 0; i < directedEdgeList.size(); i++) {
tmpE = directedEdgeList.get(i);
sb.append(tmpE.getStartV() + "->");
}
sb.append(tmpE.getEndV() + " " + sumWeight());
return sb.toString();
}
}
创建类型:
最短路径结构:使用 Map 描述到某顶点(Key:Vertex)的最短路径(Value:Path)
import java.util.*;
public class MinPathMap extends HashMap<Vertex, Path> {
public MinPathMap(int expectedSize) {
super(((int) (expectedSize / 0.75F + 1.0F))); // 此式子用来创建足够大的初始容量,以减少扩容带来的效率损失
}
/*
* 对 Value 根据提供的比较器进行排序,返回指定 index 的 Key
* */
public Vertex sortByValueRtnVByIndex(int index) {
List<Map.Entry<Vertex, Path>> entryList = new ArrayList<>(this.entrySet());
Collections.sort(entryList, new MapValueComparator());
if (index < entryList.size()) {
return entryList.get(index).getKey();
}
// index 超出了 Map 中元素的数量,返回 null
return null;
}
/*
* 比较器:以 Value 的 sumWeight 作为比较对象
* */
class MapValueComparator implements Comparator<Map.Entry<Vertex, Path>> {
@Override
public int compare(Map.Entry<Vertex, Path> o1, Map.Entry<Vertex, Path> o2) {
return o1.getValue().sumWeight() - o2.getValue().sumWeight();
}
}
/*
* 返回 Map 中所有 Path 的 toString 形式
* */
@Override
public String toString() {
return this.values().toString();
}
}
Dijkstra 算法类:传入 Graph 进行构造,通过 doDijkstraByStartV(startV) 方法获得某起点的 MinPathMap
import java.util.ArrayList;
import java.util.List;
public class Dijkstra {
private Graph graph;
public Dijkstra(Graph graph) {
this.graph = graph;
}
/*
* 获取顶点 startV 的最短路径结构
* */
public MinPathMap doDijkstraByStartV(Vertex startV) {
MinPathMap minPathMap = new MinPathMap(graph.getVertexList().size());
Boolean hasPath = initMinPathMapFromStartV(minPathMap, startV);
if (!hasPath) { // 该起点是孤立点,没有任何可达顶点,返回空的 MinPathMap
return minPathMap;
}
// 一个顶点可能与所有点都有连接,则至多进行 graph 的顶点数次路径松弛操作(新最短路径替换旧最短路径)
for (int i = 0; i < graph.getVertexList().size(); i++) {
Vertex nextStartV = minPathMap.sortByValueRtnVByIndex(i);
if (nextStartV == null) { // 所有连接点的路径已访问完,跳出循环
break;
}
// 依次(从小到大)访问各个最短路径连接点,查找是否有 newPath 添加或是替换 oldPath 成为新的最短路径,将寻找全局最优分解为多个寻找局部最优的问题
List<DirectedEdge> fromNextStartVList = findDirectedEsFromNextStartV(nextStartV);
for (int j = 0; j < fromNextStartVList.size(); j++) {
DirectedEdge minWeightNextE = fromNextStartVList.get(j);
Path newPath = new Path(minPathMap.get(minWeightNextE.getStartV()).getDirectedEdgeList());
newPath.addPath(minWeightNextE);
int newPathWeight = minPathMap.get(nextStartV).sumWeight() + minWeightNextE.getWeight();
Path oldPath = minPathMap.get(minWeightNextE.getEndV());
// newPath 的添加或是替换
minPathMap.put(minWeightNextE.getEndV(), oldPath == null? newPath : oldPath.sumWeight() > newPathWeight? newPath : oldPath);
}
}
return minPathMap;
}
/*
* 对 MinPathMap 做初始化,添加经由 startV 的主路径集
* */
private Boolean initMinPathMapFromStartV(MinPathMap minPathMap, Vertex startV) {
Boolean hasPath = false;
List<DirectedEdge> directedEdgeList = graph.getDirectedEdgeList();
DirectedEdge directedEdge = null;
for (int i = 0; i < directedEdgeList.size(); i++) {
directedEdge = directedEdgeList.get(i);
if (directedEdge.getStartV().equals(startV)) {
hasPath = true;
minPathMap.put(directedEdge.getEndV(), new Path(directedEdge));
}
}
return hasPath;
}
/*
* 寻找经由 startV 的次路径集
* */
private List<DirectedEdge> findDirectedEsFromNextStartV(Vertex nextStartV) {
List<DirectedEdge> fromStartV = new ArrayList<>();
List<DirectedEdge> directedEdges = graph.getDirectedEdgeList();
DirectedEdge directedEdge = null;
for (int i = 0; i < directedEdges.size(); i++) {
directedEdge = directedEdges.get(i);
if (directedEdge.getStartV().equals(nextStartV)) {
fromStartV.add(directedEdge);
}
}
return fromStartV;
}
}
测试用例:
Test:
public class Test {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
DirectedEdge e1 = new DirectedEdge(v1, v3, 10);
DirectedEdge e2 = new DirectedEdge(v1, v5, 30);
DirectedEdge e3 = new DirectedEdge(v1, v6, 100);
DirectedEdge e4 = new DirectedEdge(v2, v3, 5);
DirectedEdge e5 = new DirectedEdge(v3, v4, 50);
DirectedEdge e6 = new DirectedEdge(v4, v6, 10);
DirectedEdge e7 = new DirectedEdge(v5, v4, 20);
DirectedEdge e8 = new DirectedEdge(v5, v6, 60);
Graph graph = new Graph();
graph.addDirectedE(e1);
graph.addDirectedE(e2);
graph.addDirectedE(e3);
graph.addDirectedE(e4);
graph.addDirectedE(e5);
graph.addDirectedE(e6);
graph.addDirectedE(e7);
graph.addDirectedE(e8);
Dijkstra dijkstra = new Dijkstra(graph);
System.out.println(dijkstra.doDijkstraByStartV(v1));
}
}
输出结果:
[v1->v5->v4->v6 60, v1->v5 30, v1->v3 10, v1->v5->v4 50]