一、图的存储方式总结
存图可以三种办法:
(1)点到点:邻接矩阵
(2)点+点的出边:邻接表
(3)只保存边:\(Edge\)结构体
适合场景:
邻接矩阵
(1)要判断任意两顶点是否有边无边就很容易了;
(2)要知道某个顶点的度,其实就是这个顶点\(v_i\)在邻接矩阵中第\(i\)行或(第\(i\)列)的元素之和;
(3)求顶点\(v_i\)的所有邻接点就是将矩阵中第\(i\)行元素扫描一遍,\(g[i][j]\)为\(1\)就是邻接点;
而有向图讲究入度和出度,顶点\(v_i\)的入度为\(1\),正好是第\(i\)列各数之和。顶点\(v_i\)的出度为\(2\),即第\(i\)行的各数之和。
邻接表
邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。
(1)快速知道从某个点引出多少条边
(2)无法快速知道两个点之间是否有边
(3)如果只关心枚举每条边,需要先枚举每个点,再二次循环找到每条边,不如按结构体存方便
二、Bellman_Ford+结构体
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2510; //结点数
const int M = 6200 * 2 + 10; //边数
struct Edge {
int a, b, c;
} edges[M];
int n, m; // n个结点,m条边
int s, t; //起点,终点
int d[N]; //距离数组
void bellman_ford() {
memset(d, 0x3f, sizeof d);
d[s] = 0;
for (int i = 1; i < n; i++)
for (int j = 0; j < 2 * m; j++) {
Edge e = edges[j];
d[e.b] = min(d[e.b], d[e.a] + e.c);
}
}
int main() {
cin >> n >> m >> s >> t;
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
edges[i] = {a, b, c}, edges[m + i] = {b, a, c};
}
bellman_ford();
cout << d[t] << endl;
return 0;
}
三、Bellman_Ford+邻接表
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2510; //结点数
const int M = 6200 * 2 + 10; //边数
//邻接表
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int n, m; // n个结点,m条边
int s, t; //起点,终点
int d[N]; //距离数组
void bellman_ford() {
memset(d, 0x3f, sizeof d);
d[s] = 0;
int T = n - 1; //常规的Bellman_Ford是执行n-1次
while (T--) { //松驰n-1次
for (int k = 1; k <= n; k++) //枚举1~n节点
for (int i = h[k]; ~i; i = ne[i]) { //二层循环枚举每个节点的出边
int j = e[i];
d[j] = min(d[j], d[k] + w[i]);
}
}
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m >> s >> t;
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
bellman_ford();
cout << d[t] << endl;
return 0;
}
四、SPFA
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2510; //结点数
const int M = 6200 * 2 + 10; //边数
int n, m; // n个结点,m条边
int s, t; //起点,终点
//邻接表
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int d[N]; //最短距离数组
bool st[N]; //是不是进过队列
// spfa模板
void spfa(int start) {
queue<int> q;
//将所有距离初始化为无穷大
memset(d, 0x3f, sizeof d);
//出发点的距离清零
d[start] = 0;
q.push(start); //出发点入队列
st[start] = true; //出发点标识已使用
while (q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > d[t] + w[i]) {
d[j] = d[t] + w[i];
if (!st[j]) {
st[j] = true;
q.push(j);
}
}
}
}
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m >> s >> t;
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
spfa(s);
cout << d[t] << endl;
return 0;
}
五、Dijkstra
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2510; //结点数
const int M = 6200 * 2 + 10; //边数
int n, m; // n个结点,m条边
int s, t; //起点,终点
typedef pair<int, int> PII;
//邻接表
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int d[N]; //最短距离数组
bool st[N]; //是不是进过队列
//迪杰斯特拉
void dijkstra(int start) {
memset(d, 0x3f, sizeof d); //初始化距离为无穷大
memset(st, 0, sizeof st); //初始化为未出队列过
d[start] = 0; //出发点距离0
priority_queue<PII, vector<PII>, greater<PII>> q; //小顶堆
q.push({0, start}); //出发点入队列
while (q.size()) {
auto t = q.top();
q.pop();
int u = t.second;
if (st[u]) continue;
st[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > d[u] + w[i]) {
d[j] = d[u] + w[i];
q.push({d[j], j});
}
}
}
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m >> s >> t;
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dijkstra(s);
cout << d[t] << endl;
return 0;
}