借用一下别人的图像(真的很全面)
1976. 到达目的地的方案数 - 力扣(LeetCode)
1. 链式前向星的构建和遍历:
链式前向星的构建:
在链式前向星构建的过程中,head[N]数据指代的是以N为起点这些边的上一条边编号,每一条边的next也就是下一条边都应该指向的是上一条边,所以先用head[N]来赋值再更新。(也可以写成如下形式,先做赋值运算,cnt才会自增)
head[a] = cnt++;
class Solution {
public:
const static int MOD = 1e9 + 7; //定义取模的参量
const static int N = 2e5 + 10; // 最大边数
int cnt = 0; // 记录边的数量
struct EDGE {
long long w; // 边的权值
int to; // 这条边的终点
int next; // 这条边的上一条边
} edge[N];
int head[205]; // 每一个点对应的第一条边
void add_edge(int a, int b, long long c) {
edge[cnt].w = c;
edge[cnt].to = b;
edge[cnt].next = head[a]; //以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号
head[a] = cnt; //更新以u为起点上一条边的编号
cnt++; //更新边数,新的一条边
}
int countPaths(int n, vector<vector<int>>& roads) {
// 初始化,到-1的时候意味着这个点的所有边全部遍历
memset(head, -1, sizeof(head));
// 构造链式前向星
for (int i = 0; i < roads.size(); i++) {
add_edge(roads[i][0], roads[i][1], roads[i][2]);
add_edge(roads[i][1], roads[i][0], roads[i][2]);
}
return dijkstra(n);
}
};
链式前向星的遍历:
当第一条边时,head[N]还是在-1的情况下,所以遍历时发现为-1代表已经是最后一条边(也就是你第一条加入的边。在最开始访问的时候,head[N]指代的是最后一条边。
for (int i = head[ver]; i != -1; i = edge[i].next)
2. dijkstra算法:
利用小根堆维护可达路径的最小值,一直取最小值的那条边连接的点去更新,然后将新的端点加入到小根堆中。
下面是完整代码:
class Solution {
public:
const static int MOD = 1e9 + 7; //定义取模的参量
const static int N = 2e5 + 10; // 最大边数
int cnt = 0; // 记录边的数量
struct EDGE {
long long w; // 边的权值
int to; // 这条边的终点
int next; // 这条边的上一条边
} edge[N];
int head[205]; // 每一个点对应的第一条边
void add_edge(int a, int b, long long c) {
edge[cnt].w = c;
edge[cnt].to = b;
edge[cnt].next = head[a]; //以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号
head[a] = cnt; //更新以u为起点上一条边的编号
cnt++; //更新边数,新的一条边
}
typedef pair<long long, int> PII; // first存距离,second存结点编号
long long dist[N]; //存储起点到每个节点的距离
int pathCount[N]; //记录起点到每个点的最短路径
bool st[N]; //记录每个点是否被访问过
long long dijkstra(int n) {
memset(dist, 0x3f, sizeof(dist)); //将所有距离初始化正无穷
memset(pathCount, 0, sizeof(pathCount)); //将路径数量初始化为0
dist[0] = 0; // 第一个点到起点的距离为0
pathCount[0] = 1; //到达起点的路径数量为1
/*利用小根堆可以一直保证取出的结点是当前这些已知最短路径结点可达的最短路径*/
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 0}); //把0号点放入堆中
while (!heap.empty()) {
PII t = heap.top(); // 找到当前距离最小的点
heap.pop();
int ver = t.second; //取出这个结点的编号
long long distance = t.first; //取出当前节点到根节点已知的最短路径
if (st[ver])
continue; // 重边的话不用更新其他点了
st[ver] = true; // 标记ver已经确定最短路
/*用ver更新其他点的最短距离,ver是新加入的最短路径结点,对他相邻结点更新*/
/*链式前向星的遍历方式*/
for (int i = head[ver]; i != -1; i = edge[i].next) {
/*找到它的可达顶点*/
int j = edge[i].to;
/*发现利用可达定点更新会让值更小,更新距离*/
if (dist[j] > distance + edge[i].w) {
dist[j] = distance + edge[i].w;
/*添加了新的结点,将结点可达的所有边添加进去,筛选最近的边*/
heap.push({dist[j], j});
/*路径数量更新,走的是同一条边,数量相同*/
pathCount[j] = pathCount[ver];
}
/*距离相等,发现有新的路径,把本来能到的和新路径的数量加在一起*/
else if (dist[j] == distance + edge[i].w) {
pathCount[j] = (pathCount[j] + pathCount[ver]) % MOD; // 累加路径数量
}
}
}
/*路过路径始终未被更新,说明不存在最短路径*/
if (dist[n - 1] == 0x3f3f3f3f3f3f3f3fLL)
return 0;
return pathCount[n - 1];
}
int countPaths(int n, vector<vector<int>>& roads) {
// 初始化,到-1的时候意味着这个点的所有边全部遍历
memset(head, -1, sizeof(head));
// 构造链式前向星
for (int i = 0; i < roads.size(); i++) {
add_edge(roads[i][0], roads[i][1], roads[i][2]);
add_edge(roads[i][1], roads[i][0], roads[i][2]);
}
return dijkstra(n);
}
};