Dijkstra的堆优化
回忆朴素版本
朴素版本的dj就是在更新距离时我们用距离最近的那个点更新别的点,然后此点就不会再改变了,随后我们在未确定最短距离的点中寻找最近的距离的点,再用它更新别的点,重复以上步骤。
堆优化
不难看出,在从未确定最短距离的点中寻找最近距离的点这步要是通过暴力写的话,复杂度一定是很高的,所以我们用小根堆来写,可以直接用logn的复杂度提取出距离最近的点去更新别的点。
为什么用最近的点更新别的点
很好理解,因为最近的点不会再被别的点更新,所以用他更新完别的点以后就不用再考虑这个点了。
朴素代码
void Dijkstra()
{
memset(dist, 0x3f, sizeof(dist));//dist 数组的各个元素为无穷大
dist[1] = 0;//源点到源点的距离为置为 0
for (int i = 0; i < n; i++)
{
int t = -1;
for (int j = 1; j <= n; j++)//遍历 dist 数组,找到没有确定最短路径的节点中距离源点最近的点t
{
if (!state[j] && (t == -1 || dist[j] < dist[t]))
t = j;
}
state[t] = 1;//state[i] 置为 1。
for (int j = h[t]; j != -1; j = ne[j])//遍历 t 所有可以到达的节点 i
{
int i = e[j];
dist[i] = min(dist[i], dist[t] + w[j]);//更新 dist[j]
}
}
}
优化代码
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);//距离初始化为无穷大
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;//小根堆
heap.push({0, 1});//插入距离和节点编号
while (heap.size())
{
auto t = heap.top();//取距离源点最近的点
heap.pop();
int ver = t.second, distance = t.first;//ver:节点编号,distance:源点距离ver 的距离
if (st[ver]) continue;//如果距离已经确定,则跳过该点
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])//更新ver所指向的节点距离
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});//距离变小,则入堆
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}