普通迪杰斯特拉算法的复杂度为O(n^2),因为每次寻找当前最短路都要对所有的点进行遍历。当数据量大于1e5时,很有可能就会超时,因此可以针对寻找当前最短路的方法进行堆优化。
优化的思路就是建立一个优先队列,每次更新路径时将点压入,取最短路时直接取根就行。复杂度为O(eloge) 与边数有关。
链式前向星是邻接矩阵的一种保存方式,优点是可以携带更多的信息,比vector更快,使用的内存更少。
目录
1.链式前向星
以下是个人的理解:
每个点都用一条链表来记录与之连接的点和边权,链表的插入方式为前插法。
先介绍链式前向星用到的两个数组:
1.edge:
定义如下:
struct edge
{
int to; //与之连接的点的编号
int dist; //边长
int next; //另一条边的下标
};
一个点可以连接多条边,因此next的作用就是指向另一条共起点的边的下标,形成了一条链表。
2.head:
head[n]表示点n在这条链表中头节点的下标。如果没有点与其相连,则head[n]为-1(具体是多少跟题目有关,也可能为0)。
然后是关于如何添加边:
edge eg[MAXE];
int head[MAXN];
int cnt = 0;
void add_edge(int from, int to, int dist)
{
eg[++cnt].to = to;
eg[cnt].dist = dist;
eg[cnt].next = head[from]; //下一节点更新为旧的头节点,如果链表为空,则next为-1
head[from] = cnt; //更新头节点
}
最后是关于如何遍历
for (int i = head[origin]; i != -1; i = eg[i].next)
{
//....具体操作
}
以上就是关于链式前向星的全部介绍了,在数据量过大以致邻接矩阵会内存超限时建议使用链式前向星。
2. 最短路的堆优化
洛谷例题
这题如果用普通的Dijkstra算法会超时
#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
const int inf = 2e9 + 5;
const int N = 1e5 + 5;
using namespace std;
struct edge //链式前向星方式存边
{
int to; //与之有边相连的点
int next; //下一条边的下标
int distance; //边权
} e[N * 2];
struct node //堆优化的Dijkstra算法状态记录结构体
{
int index;
int dis;
node() {}
node(int i, int d) { index = i, dis = d; }
};
bool operator<(node a, node b) { return a.dis > b.dis; } //运算符重载,用于实现优先队列的排序
int head[N], cnt = 0;
int dis[N];
bool vis[N];
void addEdge(int u, int v, int dis)
{
e[++cnt].to = v;
e[cnt].distance = dis;
e[cnt].next = head[u];
head[u] = cnt;
}
void Dijkstra(int s, int n)
{
for (int i = 1; i <= n; i++)
dis[i] = inf;
mem(vis, 0);
priority_queue<node> q;
q.push(node(s, 0));
dis[s] = 0;
while (q.size())
{
int x = q.top().index; //取堆顶的点作为下一个确定的点
int d = q.top().dis;
q.pop();
if (vis[x]) continue; // x是确定的点,跳过
vis[x] = 1;
for (int i = head[x]; i; i = e[i].next)
{
int to = e[i].to;
if (vis[to]) continue; //已经确定的点就跳过
if (d + e[i].distance < dis[to])
{
dis[to] = d + e[i].distance;
q.push(node(to, dis[to])); //将更新的点状态入堆
}
}
}
}
int main()
{
mem(head, 0);
int n, m, s, u, v;
int w;
scanf("%d%d%d", &n, &m, &s);
while (m--)
{
scanf("%d%d%d", &u, &v, &w);
addEdge(u, v, w); //添加有向边
}
Dijkstra(s, n);
for (int i = 1; i <= n; i++)
printf("%d ", dis[i]);
return 0;
}