迪杰斯特拉求解最短路径

package trains;

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

import trains.bean.City;
import trains.bean.ToOherCityRoute;
import trains.util.MyAssert;

/**
 * 铁路线路图
 * @author xujw
 * 2019年3月9日20:03:05
 */
public class TrainsMap {
	
	/**
	 * 铁路中包含的所有城市
	 */
	private Map<String,City> allCity = new HashMap<String,City>();
	
	/**
	 * 城市间的所有路线
	 */
	private Map<String,Double> allRoute = new HashMap<String,Double>();
	
	/**
	 * 向地图里添加一个路线
	 * @param fromCityName 开始城市
	 * @param toCityName 终点城市
	 * @param distance 路线距离
	 */
	public void addRoute(String fromCityName,String toCityName,double distance) {
		MyAssert.isNotBlank(fromCityName, "起始城市不能为空!");
		MyAssert.isNotBlank(toCityName, "终点城市不能为空!");
		MyAssert.isTrue(distance > 0, "两城市间的距离必须大于0");
		
		Double route = allRoute.get(fromCityName+"-"+toCityName);
		if(route != null) {
			System.out.println("警告:"+fromCityName+"到"+toCityName+"的路线已经存在不需要重复添加!");
			return;
		}
		
		City fromCity = allCity.get(fromCityName);
		if(fromCity == null) {
			fromCity = new City(fromCityName);
		}
		City toCity = allCity.get(toCityName);
		if(toCity == null) {
			toCity = new City(toCityName);
		}
		
		ToOherCityRoute toRoute = new ToOherCityRoute(toCityName, distance);
		fromCity.addRoute(toRoute);
		
		allCity.put(fromCityName, fromCity);
		allCity.put(toCityName, toCity);
		allRoute.put(fromCityName+"-"+toCityName,distance);
		System.out.println("信息:"+fromCityName+"到"+toCityName+"的路线添加成功。");
	}
	
	/**
	 * 给定一个路线 计算出路线距离
	 * @param cityeNames 路线以此经过的城市节点
	 * @return 该路线距离
	 */
	public double getRouteDistance(String ...cityeNames) {
		int length = cityeNames.length;
		MyAssert.isTrue(length>1, "路线最少需要2个节点");
		
		double rs = 0;
		for(int i=1;i<length;i++) {
			Double route = allRoute.get(cityeNames[i-1]+"-"+cityeNames[i]);
			MyAssert.isNotBlank(route, "NO SUCH ROUTE");
			rs = rs+route;
		}
		return rs;
	}
	
	/**
	 * 两个城市  的路线个数  ps:允许走重复路
	 * @param fromCityName 开始城市
	 * @param toCityName 终点城市
	 * @param maxSiteCount 路线经过的站点不能超过 maxSiteCount
	 * @return 路线个数
	 */
	public int getRouteCount(String fromCityName,String toCityName,int maxStopsCount,boolean isExactly) {
		MyAssert.isNotBlank(fromCityName, "起始城市不能为空!");
		MyAssert.isNotBlank(toCityName, "终点城市不能为空!");
		City fromCity = allCity.get(fromCityName);
		MyAssert.isNotBlank(fromCity, "起始城市不存在");
		City toCity = allCity.get(toCityName);
		MyAssert.isNotBlank(toCity, "终点城市不存在");
		MyAssert.isTrue(maxStopsCount>1, "最大站点数必须大于1");
		
		//当前已经探索的 站点数
		int tempStops = 0;
		//已经计算出的路线个数
		int calRouteCount = 0;
		return calRouteCount(calRouteCount, tempStops, fromCity, toCity, maxStopsCount,isExactly);
	}
	
	/**
	 * 递归算 路径个数  由于题目只需要算个数不需要具体路径 所有相对简单点,如果需要路径需要用到回溯算法
	 * @param calRouteCount 当前计算出的路径个数
	 * @param tempStops 当前试探的站点数
	 * @param fromCity 当前位于的站点
	 * @param toCity 要去的站点
	 * @param maxStopsCount 允许经过的最大站点
	 * @return 路径个数
	 */
	private int calRouteCount(int calRouteCount,int tempStops,City fromCity,City toCity,int maxStopsCount,boolean isExactly) {
		//当前探索的步数已经达到允许的站点数 则直接返回 当前探索出的路线个数
		if(isExactly && tempStops >= maxStopsCount) {
			return calRouteCount;
		}
		if(!isExactly && tempStops >= maxStopsCount) {
			return calRouteCount;
		}
		List<ToOherCityRoute> routes = fromCity.getCanReachCity();
		for(int i=0;i<routes.size();i++) {
			ToOherCityRoute tempRoute = routes.get(i);
			String tempRouteToCityName = tempRoute.getOherCityName();
			
			/*  如果需要不能走重复路线 则这个需要还原 hadGoCity 为方法的入参 类型:Map<String,City>
			//站点已经走过  则放弃
			if(hadGoCity.get(tempRouteToCityName) != null) {
				continue;
			}
			hadGoCity.put(tempRouteToCityName, allCity.get(tempRouteToCityName));
			*/
			
			//探索到一个路径
			if(toCity.getName().equals(tempRouteToCityName)) {
				if(isExactly && tempStops+1 == maxStopsCount) {
					calRouteCount = calRouteCount+1;
				}
				if(!isExactly) {
					calRouteCount = calRouteCount+1;
				}
			}
			//继续探索
			calRouteCount = calRouteCount(calRouteCount, tempStops+1, allCity.get(tempRouteToCityName), toCity, maxStopsCount,isExactly);
		}
		return calRouteCount;
	}
	
	/**
	 * 地接斯科拉算法 计算两点间的最短距离      根据题目需要起点和终点允许相同即走环路
	 * @param fromCityName 起点城市名称
	 * @param toCityName 终点城市名称
	 * @return 返回null 表示没有路线可以到达 否则返回的是最短距离
	 */
	public Double minDistance(String fromCityName,String toCityName) {
		MyAssert.isNotBlank(fromCityName, "起始城市不能为空!");
		MyAssert.isNotBlank(toCityName, "终点城市不能为空!");
		City fromCity = allCity.get(fromCityName);
		MyAssert.isNotBlank(fromCity, "起始城市不存在");
		City toCity = allCity.get(toCityName);
		MyAssert.isNotBlank(toCity, "终点城市不存在");
		Map<String,String> hadVisitor = new HashMap<String,String>();
		Map<String,String> preCity = new HashMap<String,String>();
		Map<String,Double> toCityMinDistance = new HashMap<String,Double>();
		toCityMinDistance = calNextCityDijsktra(fromCityName, 0,hadVisitor,preCity,toCityMinDistance);
		printMinRoute(fromCityName,toCityMinDistance,preCity);
		//如果两点相同特殊处理    依次计算该节点 下一节点回到该节点距离 然后加上该节点到下一点距离 取最小
		if(fromCityName.equals(toCityName)) {
			Double minDistance = null;
			List<ToOherCityRoute> routes = fromCity.getCanReachCity();
			for(int i=0;i<routes.size();i++) {
				ToOherCityRoute route = routes.get(i);
				Double tempDistance = minDistance(route.getOherCityName(), toCityName);
				if(tempDistance != null && (minDistance == null || (tempDistance+route.getDistance()<minDistance))) {
					minDistance = tempDistance+route.getDistance();
				}
			}
			return minDistance;
		}
		return toCityMinDistance.get(toCityName);
	}
	
	
	
	/**
	 * 地接斯科拉递归实现 
	 * @param tempCityName 每次计算得到的最小距离节点
	 * @param calTempDistance 当前节点的权重
	 * @param hadVisitor 已经处理过的集合 即 s  s:地杰斯特拉已经计算的集合 t为非s集合
	 * @param preCity 节点的前驱
	 * @param hadToCityMinDistance 已计算节点的最短路径
	 * @return
	 */
	private Map<String,Double> calNextCityDijsktra(String tempCityName,double calTempDistance,Map<String,String> hadVisitor,Map<String,String> preCity,Map<String,Double> hadToCityMinDistance) {
		City tempCity = allCity.get(tempCityName);
		List<ToOherCityRoute> routes = tempCity.getCanReachCity();
		
		hadToCityMinDistance.put(tempCityName, calTempDistance);
		for(int i=0;i<routes.size();i++) {
			ToOherCityRoute tempRoute = routes.get(i);
			String toCityName = tempRoute.getOherCityName();
			if(hadToCityMinDistance.get(toCityName) == null || hadToCityMinDistance.get(toCityName) > calTempDistance+tempRoute.getDistance()) {
				hadToCityMinDistance.put(toCityName, calTempDistance+tempRoute.getDistance());
				preCity.put(toCityName, tempCityName);
			}
		}
		
		Double tempMinDistance = null;
		String minCityName = null;
		for(String key:hadToCityMinDistance.keySet()) {
			if(hadVisitor.get(key) == null && (tempMinDistance == null || tempMinDistance> hadToCityMinDistance.get(key))) {
				tempMinDistance = hadToCityMinDistance.get(key);
				minCityName = key;
			}
		}
		if(minCityName != null) {
			hadVisitor.put(minCityName, tempCityName);
			hadToCityMinDistance = calNextCityDijsktra(minCityName,tempMinDistance, hadVisitor,preCity, hadToCityMinDistance);
		}
		return hadToCityMinDistance;
	}
	
	/**
	 * 输出计算后的路径
	 * @param fromCity 开始城市
	 * @param toCityMinDistance 可以到达城市的距离
	 * @param hadVisitor 每个节点的前驱
	 */
	private void printMinRoute(String fromCity,Map<String,Double> toCityMinDistance,Map<String,String> preCity) {
		for(String key:toCityMinDistance.keySet()) {
			List<String> routes = new ArrayList<String>();
			String tempEnd = key;
			while(preCity.get(tempEnd) != null) {
				if(fromCity.equals(tempEnd)) {
					break;
				}
				routes.add(tempEnd);
				tempEnd = preCity.get(tempEnd);
				
			}
			StringBuilder routeStr = new StringBuilder(fromCity+"->"+key+"距离"+toCityMinDistance.get(key)+",路径:");
			routeStr.append(fromCity);
			for(int i=0;i<routes.size();i++) {
				routeStr.append("->"+routes.get(routes.size()-i-1));
			}
			System.out.println(routeStr);
		}
	}
	
	/**
	 * 在规定距离内 两点间路线个数
	 * @param fromCityName 起点城市
	 * @param toCityName 终点城市
	 * @param maxDistance 允许的最大距离
	 * @return 返回路线个数
	 */
	public int getRouteCountLessDistance(String fromCityName,String toCityName,double maxDistance) {
		MyAssert.isNotBlank(fromCityName, "起始城市不能为空!");
		MyAssert.isNotBlank(toCityName, "终点城市不能为空!");
		City fromCity = allCity.get(fromCityName);
		MyAssert.isNotBlank(fromCity, "起始城市不存在");
		City toCity = allCity.get(toCityName);
		MyAssert.isNotBlank(toCity, "终点城市不存在");
		MyAssert.isTrue(maxDistance>0, "允许距离必须大于0");
		
		//当前已经探索的 距离
		double tempDistance = 0;
		//已经计算出的路线个数
		int calRouteCount = 0;
		return calRouteDistance(calRouteCount, tempDistance, fromCity, toCity, maxDistance);
	}
	
	/**
	 * 递归求路线个数
	 * @param calRouteCount 当前计算出的路径个数
	 * @param tempDistance 当前已经走了的距离
	 * @param fromCity 当前所在城市
	 * @param toCity 目标城市
	 * @param maxDistance 允许的最大距离
	 * @return 范湖路线个数
	 */
	private int calRouteDistance(int calRouteCount,double tempDistance,City fromCity,City toCity,double maxDistance) {
		//当前探索的路径长度已经达到允许的路径长度 则直接返回 当前探索出的路线个数
		if(tempDistance >= maxDistance) {
			return calRouteCount;
		}
		List<ToOherCityRoute> routes = fromCity.getCanReachCity();
		for(int i=0;i<routes.size();i++) {
			ToOherCityRoute tempRoute = routes.get(i);
			String tempRouteToCityName = tempRoute.getOherCityName();
			
			//探索到一个路径
			if(toCity.getName().equals(tempRouteToCityName) && tempDistance+tempRoute.getDistance() < maxDistance) {
				calRouteCount = calRouteCount+1;
			}
			//继续探索
			calRouteCount = calRouteDistance(calRouteCount, tempDistance+tempRoute.getDistance(), allCity.get(tempRouteToCityName), toCity, maxDistance);
		}
		return calRouteCount;
	}
	
	public static void main(String[] args) {
		TrainsMap app = new TrainsMap();
		app.addRoute("A", "B", 5);
		app.addRoute("B", "C", 4);
		app.addRoute("C", "D", 8);
		app.addRoute("D", "C", 8);
		app.addRoute("D", "E", 6);
		app.addRoute("A", "D", 5);
		app.addRoute("C", "E", 2);
		app.addRoute("E", "B", 3);
		app.addRoute("A", "E", 7);
		System.out.println("A-B-C 路线距离为:"+app.getRouteDistance("A","B","C"));
		System.out.println("A-D 路线距离为:"+app.getRouteDistance("A","D"));
		System.out.println("A-D-C 路线距离为:"+app.getRouteDistance("A","D","C"));
		System.out.println("A-E-B-C-D 路线距离为:"+app.getRouteDistance("A","E","B","C","D"));
		try {
			System.out.println("A-E-D 路线距离为:"+app.getRouteDistance("A","E","D"));
		}
		catch(Exception e) {
			System.out.println("A-E-D 路线距离为:"+e.getMessage());
		}
		System.out.println("C到C经过站点个数不超过3的路线个数:"+app.getRouteCount("C", "C",3,false));
		System.out.println("A到C经过站点个数为4的路线个数:"+app.getRouteCount("A", "C",4,true));
		
		System.out.println("A到C最短路径:"+app.minDistance("A", "C"));
		System.out.println("B到B最短路径:"+app.minDistance("B", "B"));
		
		System.out.println("C到C路径长度不超过 30的路线个数:"+app.getRouteCountLessDistance("C","C",30));
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值