dijksta每次都需要从所有的距离最小的点中选择一个进行扩展。而进行选择距离最下的点的时候使用的是遍历,每次遍历找出最下的那个点。这样的效率肯定是不高的。
我们可以使用一个小根堆结构,将所有的节点放入小根堆中,然后每次取出堆顶元素即可。每次更新的时候需要将小根堆中的元素的值进行更改。这显然无法使用系统提供的堆结构。所以我们只有手动写堆结构来实现。
//弹出的节点和距离信息
public static class NodeRecord {
public Node node;
public int distance;
public NodeRecord(Node node,int distance) {
this.node = node;
this.distance = distance;
}
}
//堆结构
public static class NodeHeap{
private Node[] nodes;//堆中的点集,堆的底层是数组结构
private HashMap<Node,Integer> heapIndexMap;//点以及每个点在数组中的位置。
private HashMap<Node,Integer> distanceMap;//点和源点的距离
private int size;
public NodeHeap(int size) {
nodes = new Node[size];
heapIndexMap = new HashMap<Node, Integer>();
distanceMap = new HashMap<Node, Integer>();
this.size = 0;
}
private boolean isEmpty() {
return this.size == 0;
}
//是否进来过小根堆
private boolean isEntered(Node node) {
return heapIndexMap.containsKey(node);
}
//heapIndex.get(node) == -1表示的是进来过小根堆,但是以及被弹出的节点
private boolean inHeap(Node node) {
return isEntered(node) && heapIndexMap.get(node) != -1;
}
private void swap(int index1,int index2) {
//交换数组中两个位置的值,在记录位置的map数组中也要做出相应的交换
heapIndexMap.put(nodes[index1], index2);
heapIndexMap.put(nodes[index2],index1);
Node tmp = nodes[index1];
nodes[index1] = nodes[index2];
nodes[index2] = tmp;
}
private void insertHeapify(int index) {
//小根堆往上走
while(distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index-1)/2])) {
swap(index,(index-1)/2);
index = (index-1) / 2;
}
}
private void heapify(int index,int size) {
int left = index * 2 + 1;
while(left<size) {
int smallest = left + 1 < size && distanceMap.get(nodes[left+1]) < distanceMap.get(nodes[left])? left+1:left;
smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest :index;
if(smallest == index) {
break;
}
else {
swap(smallest,index);
index = smallest;
left = index *2 + 1;
}
}
}
public void addOrUpdateOrIgnore(Node node,int distance) {
//在堆中如果距离变小的就往上走
if(inHeap(node)) {
distanceMap.put(node, Math.min(distanceMap.get(node),distance));
insertHeapify(heapIndexMap.get(node));
}
//如果从来没有出现在堆中就新建一个点。
if(!isEntered(node)) {
nodes[size] = node;
heapIndexMap.put(node,size);
distanceMap.put(node,distance);
insertHeapify(size++);
}
//如果之前出现在堆中,但是以及被弹出了,就不用管了。因为距离一定是最小的了,不用考虑
}
public NodeRecord pop() {
int distance = distanceMap.get(nodes[0]);
Node node = nodes[0];
swap(0,size-1);
//-1表示之前在堆中,但是已经被弹出,记录一下。
heapIndexMap.put(node, -1);
distanceMap.remove(node);
nodes[size-1] = null;
heapify(0,size--);
size--;
return new NodeRecord(node, distance);
}
}