图的存储
直接存边
使用一个数组(结构体数组)来存边,数组中的每个元素都包含一条边的起点与终点(带边权的图还包含边权)。(或者使用多个数组分别存起点,终点和边权。)
应用
由于直接存边的遍历效率低下,一般不用于遍历图。
在 Kruskal 算法 中,由于需要将边按边权排序,需要直接存边。
在有的题目中,需要多次建图(如建一遍原图,建一遍反图),此时既可以使用多个其它数据结构来同时存储多张图,也可以将边直接存下来,需要重新建图时利用直接存下的边来建图。
邻接矩阵
a[i][j] = 3; 代表 i 到 j 的路程为 3
应用
邻接矩阵只适用于没有重边(或重边可以忽略)的情况。
其最显著的优点是可以 查询一条边是否存在。
由于邻接矩阵在稀疏图上效率很低(尤其是在点数较多的图上,空间无法承受),所以一般只会在稠密图上使用邻接矩阵。
邻接表
使用一个支持动态增加元素的数据结构构成的数组,如 vector<int> edge[n + 1]
来存边,其中 edge[u]
存储的是点 的所有出边的相关信息**(终点、边权等)。**
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 10000005;
int n, m;
int dist[N];
struct edge {
int to, cost;
edge(int to, int cost) {
this->to = to;
this->cost = cost;
}
};
vector<edge> e[N];
int pre[N];//记录前驱
void dij(int s){
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que; // 默认优先第一个元素排序
memset(pre, -1, sizeof pre);
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0;
que.push(make_pair(0,s));//把起点压入队列
while(!que.empty()) {
pair<int, int> p = que.top();
que.pop();
//顶点的编号
int v = p.second;
if(dist[v] < p.first) continue; // 说明这个点是被访问过的
for(int i = 0; i < e[v].size(); i++) {
edge node = e[v][i];
if(dist[node.to] > dist[v] + node.cost) {
dist[node.to] = dist[v] + node.cost;
que.push(make_pair(dist[node.to], node.to));
pre[node.to] = v; // 记录前驱节点
}
}
}
}
int main() {
int n, m;
cin >> n >> n;
for(int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
e[x].push_back(edge(y, z));
}
int s = 1;
dij(s);
for(int i = 0; i < n; i++) {
cout << dist[i] << ' ' << endl;
}
return 0;
}