代码:
package edu.stu.tptang;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
public class ShortestPath<V, E> {
private Map<V, Vertex<V, E>> vertices = new HashMap<>(); //顶点集
private Set<Edge<V, E>> edges = new HashSet<>(); //边集
private Comparator<E> comparator; //比较器
public void addEdge(V from, V to) {
addEdge(from, to, null);
}
/**
* @param from 源顶点的value值
* @param to 目的顶点的value值
* @param weight 权重
*/
public void addEdge(V from, V to, E weight) {
//拿到源顶点-判断源顶点是否已经存在
Vertex<V, E> fromVertex = vertices.get(from);
if(fromVertex == null) {
fromVertex = new Vertex<>(from);
vertices.put(from, fromVertex);
}
//拿到目的顶点-判断目的顶点是否已经存在
Vertex<V, E> toVertex = vertices.get(to);
if(toVertex == null) {
toVertex = new Vertex<>(to);
vertices.put(to, toVertex);
}
//判断要添加的边是否存在
//如果有边存在则删除,之后统一重新添加
Edge<V, E> edge = new Edge<>(fromVertex, toVertex, weight);
if(fromVertex.outEdges.remove(edge)) {
toVertex.inEdges.remove(edge);
edges.remove(edge);
}
fromVertex.outEdges.add(edge);
toVertex.inEdges.add(edge);
edges.add(edge);
}
/**
* 求单源最短路径
* @param origin 起点
* @return 起点到各个点的最短路径
*/
public Map<V, PathInfo<V, E>> shortestPaths(V origin, WeightManager<E> weightManager){
return shortestPaths(origin, weightManager, null);
}
public Map<V, PathInfo<V, E>> shortestPaths(V origin, WeightManager<E> weightManager, Comparator<E> comparator){
return dijkstra(origin,weightManager, comparator);
}
private Map<V, PathInfo<V, E>> dijkstra(V origin,WeightManager<E> weightManager, Comparator<E> comparator){
this.comparator = comparator;
//拿到源点
Vertex<V, E> originVertex = vertices.get(origin);
if(originVertex == null) return null;
Map<V, PathInfo<V, E>> selectedPaths = new HashMap<>(); //存储已经得出最短路径的点
Map<Vertex<V, E>, PathInfo<V, E>> paths = new HashMap<>(); //存储未得出最短路径的点
//初始化paths
for (Edge<V, E> edge : originVertex.outEdges) {
PathInfo<V, E> pathInfo = new PathInfo<>();
pathInfo.weight = edge.weight;
pathInfo.path.add(edge);
paths.put(edge.to, pathInfo);
}
while(!paths.isEmpty()) {
Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = getMinPath(paths);
Vertex<V, E> minVertex = minEntry.getKey();
selectedPaths.put(minVertex.value, minEntry.getValue());
paths.remove(minVertex);
//对minVertex进行松弛操作
for (Edge<V, E> edge : minVertex.outEdges) {
//如果edge.to 已经离开离开桌面就不再进行松弛--适应无向图
if(selectedPaths.containsKey(edge.to.value)) continue;
//当前minVertex的outEdges的一条edge的目的顶点的新的最短路径
E newWeight = weightManager.add(minEntry.getValue().weight, edge.weight);
//旧的最短路径
E oldWeight = paths.get(edge.to) == null ? null : paths.get(edge.to).weight;
if(oldWeight == null || compare(newWeight, oldWeight) < 0) { //更新最短路径
PathInfo<V, E> pathInfo = new PathInfo<>(); //更新路径时创建一个新的PathInfo
pathInfo.weight = newWeight;
pathInfo.path.addAll(minEntry.getValue().path);
pathInfo.path.add(edge);
paths.put(edge.to, pathInfo);
}
}
}
return selectedPaths;
}
/**
* 遍历paths集合拿到最短的路径
* @return 该最短路径对应封装的顶点
*/
private Entry<Vertex<V, E>, PathInfo<V, E>> getMinPath(Map<Vertex<V, E>, PathInfo<V, E>> paths){
Iterator<Entry<Vertex<V, E>, PathInfo<V, E>>> it = paths.entrySet().iterator();
Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = it.next();
while(it.hasNext()) {
Entry<Vertex<V, E>, PathInfo<V, E>> entry = it.next(); //注意:一次循环注意使用一次it.next()
if(compare(entry.getValue().weight, minEntry.getValue().weight) < 0) minEntry = entry;
}
return minEntry;
}
private int compare(E e1, E e2) { //默认要求 E 是具有可比较性的
if(comparator != null) { //有比较器
return comparator.compare(e1, e2);
}
return ((Comparable<E>)e1).compareTo(e2); //外部自定义对象内部默认实现
}
//定义权重管理器
public interface WeightManager<E>{
E add(E w1, E w2);
}
//封装顶点
private static class Vertex<V, E>{
V value;
Set<Edge<V, E>> inEdges = new HashSet<>();
Set<Edge<V, E>> outEdges = new HashSet<>();
Vertex(V value){
this.value = value;
}
@Override
public boolean equals(Object obj) {
return Objects.equals(value, ((Vertex<V, E>)obj).value);
}
@Override
public int hashCode() {
return value == null ? 0 : value.hashCode();
}
}
//封装边
private static class Edge<V, E>{
E weight;
Vertex<V, E> from; //源顶点
Vertex<V, E> to; //目的顶点
Edge(Vertex<V, E> from, Vertex<V, E> to){
this.from = from;
this.to = to;
}
Edge(Vertex<V, E> from, Vertex<V, E> to, E weight){
this(from, to);
this.weight = weight;
}
//!!!边集用Set、顶点集Map存储 底层都是hash表+链表+红黑树 必须在Edge、Vertex类里实现各自的HashCode和equals方法
@Override
public boolean equals(Object obj) {
Edge<V, E> edge = (Edge<V, E>)obj;
//如果两条边源顶点和目的顶点都相同则视为同一条边
return Objects.equals(this.from, edge.from) && Objects.equals(this.to, edge.to);
}
@Override
public int hashCode() {
return from.hashCode() * 31 + to.hashCode();
}
}
//封装路径信息
public static class PathInfo<V, E>{
private E weight;
private List<Edge<V, E>> path = new LinkedList<>(); //用链表存储路径
public String getPathInfo() {
StringBuilder s = new StringBuilder();
for (Edge<V, E> edge : path) {
s = s.append(edge.from.value +"-->" + edge.to.value + " ");
}
return s.toString();
}
public E getWeight() {
return weight;
}
}
}
测试:
public class Test {
public static void main(String[] args) {
ShortestPath<String, Integer> s = new ShortestPath<>();
s.addEdge("A", "E", 100); //添加边时发现顶点不存在自动创建顶点
s.addEdge("A", "D", 30);
s.addEdge("A", "B", 10);
s.addEdge("B", "C", 50);
s.addEdge("D", "C", 20);
s.addEdge("D", "E", 60);
s.addEdge("C", "E", 10);
Map<String, PathInfo<String, Integer>> result = s.shortestPaths("A", new WeightManager<Integer>() {
@Override
public Integer add(Integer w1, Integer w2) {
return w1 + w2;
}
}, new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
result.forEach((String key, PathInfo<String, Integer> value)->{
System.out.println("A-->" + key + " minPath: " + value.getPathInfo() + " "+ value.getWeight());
});
}
}
测试结果: