一.链式前向星
链式前向星
链式前向星通过数组模拟链表的方式存储图。它的核心思想是:
-
使用
head
数组记录每个节点的第一条边。 -
使用
next
数组记录每条边的下一条边。 -
使用
to
数组记录每条边的终点。 -
使用
weight
数组记录每条边的权重。
// 数据结构 const int MAXN = 10005; // 最大节点数 const int MAXM = 100005; // 最大边数 int head[MAXN]; // head[u] 表示节点 u 的第一条边的编号 int next[MAXM]; // next[e] 表示边 e 的下一条边的编号 int to[MAXM]; // to[e] 表示边 e 的终点 int weight[MAXM]; // weight[e] 表示边 e 的权重 int cnt = 0; // 边的计数器 // 添加边 void addEdge(int u, int v, int w) { to[cnt] = v; // 记录终点 weight[cnt] = w; // 记录权重 next[cnt] = head[u]; // 将新边插入链表头部 head[u] = cnt++; // 更新 head[u] }
二.SPFA 算法
SPFA 是 Bellman-Ford 算法的优化版本,通过队列避免了对所有边进行不必要的松弛操作。
算法思想
-
初始化:
-
设源点为
s
,初始化距离数组dist
,其中dist[s] = 0
,其余节点的距离为无穷大(INF
)。 -
将源点
s
加入队列。
-
-
松弛操作:
-
从队列中取出一个节点
u
,遍历其所有邻接边(u, v, w)
。 -
如果
dist[u] + w < dist[v]
,则更新dist[v]
,并将v
加入队列(如果v
不在队列中)。
-
-
检测负权回路:
-
如果某个节点被松弛超过
N-1
次,则说明图中存在负权回路。
#include <iostream> #include <vector> #include <queue> #include <climits> using namespace std; const int MAXN = 10005; // 最大节点数 const int MAXM = 100005; // 最大边数 const int INF = INT_MAX; // 无穷大 int head[MAXN]; // head[u] 表示节点 u 的第一条边的编号 int next[MAXM]; // next[e] 表示边 e 的下一条边的编号 int to[MAXM]; // to[e] 表示边 e 的终点 int weight[MAXM]; // weight[e] 表示边 e 的权重 int cnt = 0; // 边的计数器 void addEdge(int u, int v, int w) { to[cnt] = v; // 记录终点 weight[cnt] = w; // 记录权重 next[cnt] = head[u]; // 将新边插入链表头部 head[u] = cnt++; // 更新 head[u] } bool spfa(int n, int s, vector<int>& dist) { dist.assign(n, INF); // 初始化距离数组 dist[s] = 0; // 源点到自身的距离为 0 queue<int> q; q.push(s); // 将源点加入队列 vector<int> cntRelax(n, 0); // 记录每个节点的松弛次数 vector<bool> inQueue(n, false); // 记录节点是否在队列中 inQueue[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); inQueue[u] = false; // 遍历 u 的所有邻接边 for (int e = head[u]; e != -1; e = next[e]) { int v = to[e]; int w = weight[e]; if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; // 如果 v 不在队列中,则加入队列 if (!inQueue[v]) { q.push(v); inQueue[v] = true; // 检测负权回路 if (++cntRelax[v] >= n) { return false; // 存在负权回路 } } } } } return true; // 不存在负权回路 } int main() { int n, m, s; cout << "请输入节点数、边数和源点: "; cin >> n >> m >> s; // 初始化 head 数组 fill(head, head + n, -1); cout << "请输入每条边的起点、终点和权重: " << endl; for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; addEdge(u, v, w); // 添加边 }
-