狄克斯特拉算法
注:该文是本博主记录学习之用,没有太多详细的讲解,敬请谅解!
一、简介
狄克斯特拉算法(Dijkstra )用于计算出不存在非负权重的情况下,起点到各个节点的最短距离
二、场景
- 从A出发是否存在到达B的路径;
- 从A出发到达B的最短路径(时间最少、或者路径最少等),事实上最后计算完成后,已经得到了A到各个节点的最短路径了;
三、实现思路
- 找出“最便宜”的节点,即可在最短时间内到达的节点。
- 更新该节点对应的邻居节点的开销。
- 重复这个过程,直到每个节点都这样做了。
- 计算最终路径。
四、java实现
public class Dijkstra {
public static void main(String[] args){
final HashMap<String,Integer> 广州 = new HashMap<String,Integer>(){
{
put("上海",1);
put("北京",5);
put("武汉",3);
}
};
final HashMap<String,Integer> 上海 = new HashMap<String,Integer>(){
{
put("北京",4);
}
};
final HashMap<String,Integer> 武汉 = new HashMap<String,Integer>(){
{
put("北京",1);
put("南京",2);
}
};
final HashMap<String,Integer> 成都 = new HashMap<String,Integer>(){
{
put("上海",7);
put("北京",10);
}
};
final HashMap<String,Integer> 深圳 = new HashMap<String,Integer>(){
{
put("上海",8);
}
};
final HashMap<String,Integer> 南京 = new HashMap<String,Integer>(){
{
put("北京",3);
}
};
HashMap<String,HashMap<String,Integer>> allMap = new HashMap<String,HashMap<String,Integer>>() {
{
put("广州",广州);
put("上海",上海);
put("武汉",武汉);
put("成都",成都);
put("深圳",深圳);
put("南京",南京);
}
};
Dijkstra dijkstra = new Dijkstra();
dijkstra.handle("广州","北京",allMap);
}
/**
* 计算起点到终点最优方式
* 思路:先计算起点到相邻节点的最小,然后根据最小节点再计算其子节点,依次循环,最后得到最优的路线
* @param startKey 起点节点
* @param target 终点节点
* @param all 所有数据
*/
private void handle(String startKey,String target,HashMap<String,HashMap<String,Integer>> all) {
//存放到各个节点所需要消耗的时间
HashMap<String,Integer> costMap = new HashMap<String,Integer>();
//到各个节点对应的父节点
HashMap<String,String> parentMap = new HashMap<String,String>();
//存放已处理过的节点key,已处理过的不重复处理
HashSet<String> processed = new HashSet<String>();
//首先获取开始节点相邻节点信息
HashMap<String,Integer> start = all.get(startKey);
//添加起点到各个相邻节点所需耗费的时间等信息
for(String key:start.keySet()) {
int cost = start.get(key);
costMap.put(key, cost);
parentMap.put(key,startKey);
}
//选择最"便宜"的节点,这边即耗费时间最低的
String minCostKey = getMiniCostKey(costMap,processed);
while( minCostKey!=null ) {
System.out.println("处理节点:"+minCostKey);
HashMap<String,Integer> nodeMap = all.get(minCostKey);
if (nodeMap!=null) {
//该节点没有子节点可以处理了,末端节点
handleNode(minCostKey,nodeMap,costMap,parentMap);
}
//添加该节点到已处理结束的列表中
processed.add(minCostKey);
//再次获取下一个最便宜的节点
minCostKey = getMiniCostKey(costMap,processed);
}
if(parentMap.containsKey(target)) {
System.out.println("到目标节点:"+target+" 最低耗费:"+costMap.get(target));
List<String> pathList = new ArrayList<String>();
String parentKey = parentMap.get(target);
while (parentKey!=null) {
pathList.add(0, parentKey);
parentKey = parentMap.get(parentKey);
}
pathList.add(target);
String path="";
for(String key:pathList) {
path = path + key + " --> ";
}
System.out.println("路线为"+path);
} else {
System.out.println("不存在到达"+target+"的路径");
}
}
/**
* 计算耗费最小的节点信息
* @param costs 需要计算的内容
* @param processed 处理过节点的集合
* @return
*/
private String getMiniCostKey(HashMap<String,Integer> costs, HashSet<String> processed) {
int mini = Integer.MAX_VALUE;
String miniKey = null;
for(String key : costs.keySet()) {
if(!processed.contains(key)) {
int cost = costs.get(key);
if(mini > cost) {
mini = cost;
miniKey = key;
}
}
}
return miniKey;
}
/**
* 计算子节点的耗费
* @param startKey 当前节点
* @param nodeMap 当前节点子节点数据
* @param costMap 起点节点下的数据
* @param parentMap 起点到该节点的内容
*/
private void handleNode(String startKey,HashMap<String,Integer> nodeMap,HashMap<String,Integer> costMap,HashMap<String,String> parentMap) {
for (String key : nodeMap.keySet()) {
//获取原本到父节点所需要花费的时间
int hasCost = costMap.get(startKey);
//获取父节点到子节点所需要花费的时间
int cost = nodeMap.get(key);
//计算从最初的起点到该节点所需花费的总时间
cost = hasCost + cost;
if (!costMap.containsKey(key)) {
//如果原本并没有计算过其它节点到该节点的花费
costMap.put(key, cost);
parentMap.put(key, startKey);
} else {
//获取原本耗费的时间
int oldCost = costMap.get(key);
if (cost < oldCost) {
//新方案到该节点耗费的时间更少
//更新到达该节点的父节点和消费时间对应的散列表
costMap.put(key, cost);
parentMap.put(key, startKey);
System.out.println("更新节点:" + key + ",cost:" + oldCost + " --> " + cost);
}
}
}
}
}
五、小结
1、狄克斯特拉算法Dijkstra主要用于有权重的方向图中搜索出最短路径,但不适合于有负权重的情况,标志好已处理的节点避免进入死循环。