1. Dijkstra算法基础与实现解析
Dijkstra算法是图论中最经典的单源最短路径算法之一,由荷兰计算机科学家Edsger W. Dijkstra于1956年提出。这个算法在路由选择、地图导航、网络流量优化等领域有着广泛的应用。我们先从最基础的实现开始,逐步深入理解其原理和优化方法。
1.1 算法核心思想
Dijkstra算法基于贪心策略,逐步确定从源点到其他各顶点的最短路径。其核心思想可以概括为:
- 初始化:设置源点到自身的距离为0,源点到其他所有顶点的距离为无穷大
- 选择当前距离源点最近的未访问顶点
- 通过该顶点更新其邻接顶点的距离(松弛操作)
- 标记该顶点为已访问
- 重复步骤2-4,直到所有顶点都被访问
这个过程中,算法维护两个关键数据结构:
- 距离数组dis[]:记录源点到每个顶点的当前最短距离
- 访问标记数组vis[]:记录哪些顶点已经确定了最短路径
1.2 朴素实现代码分析
让我们仔细分析提供的朴素实现代码(P3371):
cpp复制#include<bits/stdc++.h>
using namespace std;
long long n,m,s;
bool vis[1000005];
long long dis[1000005];
vector<pair<int ,int > > g[100000];
void di(){
memset(dis,0x3f,sizeof(dis));
dis[s] = 0;
for(int i = 1;i<=n;i++){
long long minx = 0x3f3f3f3f,u;
for(int j = 1;j <= n;j++){
if(dis[j] < minx && vis[j] == 0)minx = dis[j],u = j;
}
vis[u] = 1;
for(int j = 0;j<g[u].size();j++){
int y = g[u][j].first,z = g[u][j].second;
if(dis[y]>dis[u]+z)dis[y] = dis[u]+z;
}
}
}
这段代码有几个关键点需要注意:
- 使用邻接表
g[]存储图结构,每个顶点对应一个vector,存储其邻接顶点和边权 - 初始化距离数组dis[]为极大值(0x3f3f3f3f),源点s的距离设为0
- 外层循环执行n次,每次确定一个顶点的最短路径
- 内层循环遍历所有顶点,找到当前距离源点最近的未访问顶点u
- 对u的所有邻接顶点执行松弛操作:
dis[y] = min(dis[y], dis[u]+z)
注意:0x3f3f3f3f是一个常用的极大值表示,约为1e9,既足够大又不会在相加时溢出。在实际应用中,可以根据具体问题调整这个值。
1.3 时间复杂度分析
朴素实现的时间复杂度主要来自两部分:
- 外层循环执行n次
- 每次内层循环需要遍历所有n个顶点寻找最小值
因此总时间复杂度为O(n²),这在顶点数较多时(如n>1e4)会变得非常慢。这也是为什么需要优化版本的原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 优先队列优化实现
为了提高算法效率,我们可以使用优先队列(堆)来优化寻找最小距离顶点的过程。这就是P4779题目的实现方式。
2.1 优化思路
朴素实现中,每次都要遍历所有顶点来寻找距离最小的未访问顶点,这是O(n)的操作。如果我们能用一个数据结构快速获取最小值,就能显著提高效率。
优先队列(堆)可以在O(1)时间获取最小值,插入和删除操作是O(logn)时间。使用优先队列后,算法的时间复杂度可以降到O((n+m)logn),其中m是边数。
2.2 优化实现代码分析
让我们看看优化后的代码(P4779):
cpp复制#include<bits/stdc++.h>
using namespace std;
long long n,m,s;
bool vis[10000005];
long long dis[10000005];
struct node{
int x,dis;
friend bool operator < (node n1,node n2){
return n1.dis>n2.dis;
}
};
priority_queue<node> pq;
vector<pair<int ,int > > g[1000000];
void di(){
memset(dis,0x3f,sizeof(di
