把Dijkstra算法逻辑梳理了一下,其实原理也很清晰.
打个比方:
有很多房间,每个房间有若干扇门(门上有房号)可以通向另一个房间,而打开每扇门的成本是不一样的。我们的目标是按照一定规律去尽可能的探索更多的房间。
第一步: 我们找到当前房间中打开成本最低的那扇门。
第二步:我们进到新的房间,这个时候我们又会看到很多新的门。注意这里每扇门是有房号的,新房间的门的房号可能和之前其他房间遇到的门房号是一样的(通向同一个房间)。遇到这种情况我们需要去判断是从新房间的门打开成本低还是之前打开的成本低。更新所有能开的门的打开成本
第三步:从所有能开的门中找到成本最低的打开
重复第二步,直到遇到目标房间,搜索结束
import java.util.HashMap;
import java.util.LinkedList;
import java.util.TreeMap;
import java.util.Map.Entry;
public class Dijkstra {
private static void Log(String value) {
System.out.print(value);
}
private static void Logln(String value) {
System.out.println(value);
}
public static void main(String[] args) {
// a b c d e f
int[][] G = {{0, 7, 12, DIST_NAN, DIST_NAN, DIST_NAN}, // a
{7, 0, DIST_NAN, 3, DIST_NAN, 10}, // b
{12, DIST_NAN, 0, 12, 18, DIST_NAN}, // c
{DIST_NAN, 3, 12, 3, DIST_NAN, 8}, // d
{DIST_NAN, DIST_NAN, 18, DIST_NAN, 0, 6}, // e
{DIST_NAN, 10, DIST_NAN, 8, 6, 0}}; // f
char[] nodeName = {'a', 'b', 'c', 'd', 'e', 'f'};
int start = 4;
int end = 0;
Logln("find path from " + nodeName[start] + " to " + nodeName[end]);
LinkedList<Integer> ret = new Dijkstra().find(G, 6, start, end);
if (ret != null) {
while(!ret.isEmpty()) {
Log(">" + nodeName[ret.removeLast()]);
}
}
}
private static final int DIST_NAN = 8888;
private LinkedList<Integer> find(int[][] graph, int size, int vStart, int vEnd) {
int dist, minDist;
int nodeIndex, newNodeIndex;
Node node;
HashMap<Integer, Node> unMarkedNodes = new HashMap<>();
HashMap<Integer, Node> markedNodes = new HashMap<>();
for (int i = 0; i < size; i++) {
if (vStart == i) {
continue;
} else {
unMarkedNodes.put(i, new Node(vStart, graph[vStart][i]));
}
}
// find min dist
minDist = DIST_NAN;
newNodeIndex = -1;
for (HashMap.Entry<Integer, Node> entry: unMarkedNodes.entrySet()) {
dist = entry.getValue().dist;
if (minDist > dist) {
newNodeIndex = entry.getKey();
minDist = dist;
}
}
while(true) {
// Can not find path from vStart to vEnd.
if (newNodeIndex == -1) {
return null;
}
// remove new node from unmarked nodes.
node = unMarkedNodes.remove(newNodeIndex);
// add the new node into marked nodes.
markedNodes.put(newNodeIndex, node);
// If the new node is the target node, it means we have found the path with lowest distance from vStart to vEnd.
if (newNodeIndex == vEnd) {
break;
}
minDist = DIST_NAN;
nodeIndex = -1;
// update unmarked nodes, and find out the min distance from marked nodes(Graph) to the unmarked nodes(Graph).
for (HashMap.Entry<Integer, Node> entry: unMarkedNodes.entrySet()) {
int index = entry.getKey();
dist = graph[newNodeIndex][index] + node.dist;
// has a lower distance, if we access the node with new added node.
if (dist < entry.getValue().dist) {
entry.getValue().prev = newNodeIndex;
entry.getValue().dist = dist;
} else {
dist = entry.getValue().dist;
}
// update min dist.
if (dist < minDist) {
nodeIndex = index;
minDist = entry.getValue().dist;
}
}
// We found a new node from unmared nodes which has the lowest distance to the marked graph.
// If it is equal to -1, That means no new node was found.
newNodeIndex = nodeIndex;
}
node = markedNodes.get(vEnd);
LinkedList<Integer> list = new LinkedList<>();
if (node != null) {
list.add(vEnd);
while (node != null) {
list.add(node.prev);
node = markedNodes.get(node.prev);
}
}
return list;
}
private static class Node {
int prev;
int dist;
public Node(int prev, int dist) {
this.prev = prev;
this.dist = dist;
}
}
}