是的,Java PriorityQueue 不提供最小堆的 decrease key 操作,因此移除操作的时间复杂度为 O(N),但可以优化到 logN。
我已经实现了带有 decreaseKey 的最小堆(实际上是同时带有 increaseKey,但在这里只需要 decreaseKey)。实际数据结构是最小堆映射(HashMap 存储所有节点的索引,并通过当前顶点更新其邻居的最小路径值)。
我使用泛型实现了优化的解决方案,编码大约花费了我 3-4 小时(第一次尝试),时间复杂度为 O(logV.E)。
希望这能帮助到您!
package algo;
import java.util.*;
public class Dijkstra {
static class HeapMap<T> {
int size, ind = 0;
NodeWeight<T> arr [];
Map<T,Integer> map;
int index(T t) {
return map.get(t);
}
NodeWeight<T> node(int index) {
return arr[index];
}
static class NodeWeight<T> {
NodeWeight(T v, int w) {
nodeVal = v;
weight = w;
}
T nodeVal;
int weight;
List<T> path = new ArrayList<>();
}
public HeapMap(int s) {
size = s;
arr = new NodeWeight[size + 1];
map = new HashMap<>();
}
private void updateIndex(T key, int newInd) {
map.put(key, newInd);
}
private void shiftUp(int i) {
while(i > 1) {
int par = i / 2;
NodeWeight<T> currNodeWeight = arr[i];
NodeWeight<T> parentNodeWeight = arr[par];
if(parentNodeWeight.weight > currNodeWeight.weight) {
updateIndex(parentNodeWeight.nodeVal, i);
updateIndex(currNodeWeight.nodeVal, par);
swap(par,i);
i = i/2;
}
else {
break;
}
}
}
public void update(T nodeVal, int newWeight) {
int i = ind;
NodeWeight<T> nodeWeight = arr[map.get(nodeVal)];
int oldWt = nodeWeight.weight;
nodeWeight.weight = newWeight;
if(oldWt < newWeight) {
shiftDown(map.get(nodeVal));
}
else if(oldWt > newWeight) {
shiftUp(map.get(nodeVal));
}
}
public void insert(T nodeVal, int wt) {
NodeWeight<T> nodeWt = new NodeWeight<>(nodeVal, wt);
arr[++ind] = nodeWt;
updateIndex(nodeVal, ind);
shiftUp(ind);
}
private void swap(int i, int j) {
NodeWeight<T> tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
private void shiftDown(int i) {
while(i <= ind) {
int current = i;
int lChild = i * 2;
int rChild = i * 2 + 1;
if(rChild <= ind) {
int childInd = (arr[lChild].weight < arr[rChild].weight) ? lChild : rChild;
if(arr[childInd].weight < arr[i].weight) {
updateIndex(arr[childInd].nodeVal, i);
updateIndex(arr[i].nodeVal, childInd);
swap(childInd, i);
i = childInd;
}
}
else if(lChild <= ind && arr[lChild].weight < arr[i].weight) {
updateIndex(arr[lChild].nodeVal, i);
updateIndex(arr[i].nodeVal, lChild);
swap(lChild, i);
i = lChild;
}
if(current == i) {
break;
}
}
}
public NodeWeight<T> remove() {
if(ind == 0) {
return null;
}
map.remove(arr[1].nodeVal);
NodeWeight<T> out = arr[1];
out.path.add(arr[1].nodeVal);
arr[1] = arr[ind];
arr[ind--] = null;
if(ind > 0) {
updateIndex(arr[1].nodeVal, 1);
shiftDown(1);
}
return out;
}
}
static class Graph<T> {
void init(T... t) {
for(T z: t) {
nodes.put(z, new Node<>(z));
}
}
public Graph(int s, T... t) {
size = s;
nodes = new LinkedHashMap<>(size);
init(t);
}
static class Node<T> {
Node(T v) {
val = v;
}
T val;
Map<T, Integer> edges = new HashMap<>();
}
int size;
Map<T, Node<T>> nodes;
void addEdge(T from, T to, int wt) {
nodes.get(from).edges.put(to, wt);
}
}
private static <T> void init(Graph<T> graph, T from, HeapMap<T> heapMap) {
Graph.Node<T> fromNode = graph.nodes.get(from);
graph.nodes.forEach((k,v)-> {
if(from != k) {
heapMap.insert(k, fromNode.edges.getOrDefault(k, Integer.MAX_VALUE));
}
});
}
static class NodePathMinWeight<T> {
NodePathMinWeight(T n, List<T> p, int c) {
node = n;
path = p;
minCost= c;
}
T node;
List<T> path;
int minCost;
}
static <T> Map<T,NodePathMinWeight<T>> dijkstra(Graph<T> graph, T from) {
Map<T,NodePathMinWeight<T>> output = new HashMap<>();
HeapMap<T> heapMap = new HeapMap<>(graph.nodes.size());
init(graph, from, heapMap);
Set<T> isNotVisited = new HashSet<>();
graph.nodes.forEach((k,v) -> isNotVisited.add(k));
isNotVisited.remove(from);
while(!isNotVisited.isEmpty()) {
HeapMap.NodeWeight<T> currNodeWeight = heapMap.remove();
output.put(currNodeWeight.nodeVal, new NodePathMinWeight<>(currNodeWeight.nodeVal, currNodeWeight.path, currNodeWeight.weight));
isNotVisited.remove(currNodeWeight.nodeVal);
Map<T, Integer> neighbors = graph.nodes.get(currNodeWeight.nodeVal).edges;
neighbors.forEach((k,v) -> {
int ind = heapMap.index(k);
HeapMap.NodeWeight<T> neighbor = heapMap.node(ind);
int neighborDist = neighbor.weight;
int currentDistance = currNodeWeight.weight;
if(currentDistance + v < neighborDist) {
neighbor.path = new ArrayList<>(currNodeWeight.path);
heapMap.update(neighbor.nodeVal, currentDistance + v);
}
});
}
return output;
}
public static void main(String[] args) {
Graph<Integer> graph = new Graph<>(6,1,2,3,4,5,6);
graph.addEdge(1,2,2);
graph.addEdge(1,3,4);
graph.addEdge(2,3,1);
graph.addEdge(2,4,7);
graph.addEdge(3,5,3);
graph.addEdge(5,6,5);
graph.addEdge(4,6,1);
graph.addEdge(5,4,2);
Integer source = 1;
Map<Integer,NodePathMinWeight<Integer>> map = dijkstra(graph,source);
map.forEach((k,v) -> {
v.path.add(0,source);
System.out.println("source vertex :["+source+"] to vertex :["+k+"] cost:"+v.minCost+" shortest path :"+v.path);
});
}
}
输出-:
起点:[1] 终点:[2] 距离:2 最短路径:[1, 2]
起点:[1] 终点:[3] 距离:3 最短路径:[1, 2, 3]
起点:[1] 终点:[4] 距离:8 最短路径:[1, 2, 3, 5, 4]
起点:[1] 终点:[5] 距离:6 最短路径:[1, 2, 3, 5]
起点:[1] 终点:[6] 距离:9 最短路径:[1, 2, 3, 5, 4, 6]
q
来获取最小距离的节点,否则执行decrease-key v in Q;
没有任何意义。 - Ishtar