迪杰斯特拉算法主要特点是从起始点开始,采用贪心策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
算法流程:
1.对于给定的图,我们假定所有点都不可达,因为是单源最短路,所以我们从起点开始;
2.我们选取一个最短路长度最小的结点,遍历该点所有可达的点,判断结点到可达点的距离是否小于从起点 + 该条路径的权值,如果小于,说明我们找到了一条更短的路径,我们更新距离,为了防止重复添加,我们加入一个bool类型的判断数组,如果当前点访问过,直接continue;
3.重复第二步,直到所有点都无法更新;
时间复杂度:
1.朴素版Dijkstra
对于朴素版dijkstra,我们需要进行 n 次迭代,每次找到当前距起点最短的路径的点,然后我们用该点去更新其他点,我们可以看到时间复杂度是O();
2.堆优化版Dijkstra
对于堆优化版dijkstra,我们采用小根堆,那堆顶元素就是当前路径最小的点,我们每次取出堆顶元素,用堆顶元素来更新可达点的路径长度,并将该点加入堆中,堆每次操作是O(log(m))一共有m次堆上的插入操作,故时间复杂度为O(nlog(m));
Dijkstra不能适用于负权边
首先将点1添加到集合中标记已访问,之后选出从1到所有节点中的最短的点,将3加入集合中标记已访问,之后3就不会在更新了,但是1与2之间最短路径权值为1,发生错误。
代码实现思路
1.确定初始起点距离dis[1] = 0,其他设为无穷大。
2.遍历所有的点,每次找visit[i] = false ,也就是未被确定的且dis[i]最小的点k。
3.遍历与点k相连的点j,用min(dis[j],dis[k]+w)来更新dis[j];
4.重复2,3步骤,直到所有点被标记。
朴素dijkstra代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int inf = 2147483647;
int edges[1005][1005]; //记录边与边的权值
int dis[1010]; //用于记录原点距离该点的最短路径
int visit[1010]; //用于判断该点是否已经最优
int n, m, s;
int dis_index;
void dijkstra() {
for (int i = 1; i <= n; i++) {
dis[i] = INF;
visit[i] = 0;
}
dis[s] = 0;
for (int i = 1; i <= n; i++) { //n次循环,每次确定一个点
int dis_w = INF;
dis[1] = 0;
for (int j = 1; j <= n; j++) {
if (!visit[j] && (dis[j] < dis_w)) {
dis_w = dis[j];
dis_index = j;
}
}
visit[dis_index] = 1;
for (int j = 1; j <= n; j++) {
if (dis[j] > dis[dis_index] + edges[dis_index][j]) {
dis[j] = dis[dis_index] + edges[dis_index][j];
}
}
}
}
int main() {
memset(edges, INF, sizeof(edges));
scanf("%d%d%d", &n, &m, &s);
for (int i = 0; i < m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
edges[u][v] = w;
}
dijkstra();
for (int i = 1; i <= n; i++) {
if (dis[i] >= INF && dis[i] < inf) {
printf("%d ", inf);
} else
printf("%d ", dis[i]);
}
return 0;
}
理解了朴素dijkstra后,我们发现朴素DIJ的时间复杂度是O(n²),但这个复杂度在一些算法题里是会TLE的,所以我们需要优化一下DIJ。
明白dijkstra算法的知道算法的实现需要从头到尾扫一遍找出最小的点进行松弛操作,于是我们可以对这个扫描操作进行优化,使用小根堆,用优先队列来维护dis最小的点,以减少时间复杂度。
dijkstra堆优化代码:(堆优化+pair+邻接表)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
const int M = 500010;
const int INF = 2147483647;
//const int inf = 0x3f3f3f3f;
int n, m, s;
int u, v, w;
int dis[N];
int visit[N];
int head[N], to[M], ww[M], mynext[M];
int cnt;
int min(int a, int b) {
return a > b ? b : a;
}
typedef pair<int, int> PII;
//priority_queue<PII, vector<PII>, greater<PII> > heap;
void add(int u, int v, int w) {
to[cnt] = v;
ww[cnt] = w;
mynext[cnt] = head[u];
head[u] = cnt++;
}
void dijkstra();
int main() {
scanf("%d%d%d", &n, &m, &s);
memset(head, -1, sizeof(head));
memset(visit, 0, sizeof(visit));
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
}
dijkstra();
for (int i = 1; i <= n; i++) {
cout << dis[i] << " ";
}
}
void dijkstra() {
for (int i = 1; i <= n; i++) {
dis[i] = INF;
}
dis[s] = 0;
priority_queue<PII, vector<PII>, greater<PII> > heap;
heap.push({0, s});
while (heap.size()) {
PII k = heap.top();
int t = k.second;
int distance = k.first;
heap.pop();
if (visit[t] || dis[t] != distance) {
continue;
}
visit[t] = 1;
for (int j = head[t]; j != -1; j = mynext[j]) {
int v = to[j];
dis[v] = min(dis[v], distance + ww[j]);
if (dis[v] == distance + ww[j]) {
heap.push({dis[v], v});
}
}
}
}
478

被折叠的 条评论
为什么被折叠?



