Dijkstra算法代码实现

原文链接

代码:

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());
		});
		
	}
}

测试结果:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值