图的存储模板
图的存储
邻接矩阵
数据结构
设图的结点个数为 \(n\),定义 \(n\times n\) 的二维数组 \(g[N][N]\),其中 \(g[i][j]\) 表示结点 \(i\) 到 \(j\) 的边权。
对于带权图,\(g[i][j]=\begin{cases}w &边(i, j)的权值 \\ +\infty &边(i,j)无直接连边\end{cases}\)。
对于无权图,\(g[i][j]=\begin{cases}1 &i,j之间有连边 \\ 0 &i,j之间无连边\end{cases}\)。
初始化
对于带权图,常用
memset(g, 0x3f, sizeof(g));
对于无权图,常用
memset(g, 0, sizeof(g));
或直接定义全局变量即可。
加边
加一条边 \((from, to, w)\),其中起点为 \(from\),终点为 \(to\),权值为 \(w\)(无权图权值为 \(1\) 即可)
对于有向图
g[from][to] = w;
对于无向图
g[from][to] = w;
g[to][from] = w;
遍历结点 s 的邻接点
对于带权图
const int INF = 0x3f3f3f3f;
for (int i = 1; i <= n; i++) {
if (g[s][i] < INF) {
cout << i <<' ';
}
}
对于无权图
for (int i = 1; i <= n; i++) {
if (g[s][i] == 1) {
cout << i <<' ';
}
}
链氏前向星
数据结构
用数组模拟邻接表,需要一个表头数组 \(head[N]\),一个结构体数组 \(edge[M]\)。
\(head[i]\) 表示以 \(i\) 为起点的第一条边在 \(edge\) 数组中的位置(下标)。
\(edge[i]\) 表示第 \(i\) 条边的信息。
// 注意两个数组的大小,head 大小为结点个数,edge 大小为边数,如果是无向图要两倍
int head[N]; // 表头数组
int tote; // 当前加入的边的数量
struct EDGE {
int to, w, nxt; //边的终点,权值,同一起点的下一条边的位置
} edge[M];
初始化
tot = 0;
//如果第一条边放在下标为 1 的位置,也可以初始化成 0,后面跑 tarjan 的时候从 0 开始存找反向边吏方便
memset(head, -1, sizeof(head));
加边
加一条边 \((from, to, w)\),其中起点为 \(from\),终点为 \(to\),权值为 \(w\)(无权图权值为 \(1\) 即可,或不要 \(w\) 这个成员变量)
// 加入的第一条边放到下标为 0 的位置,所以 head 数组要初始化成 -1
void addedge(int from, int to, int w) {
edge[tote].to = to;
edge[tote].w = w;
edge[tote].nxt = head[from];
head[from] = tote;
tote++;
}
对于有向图
addedge(from, to, w);
对于无向图
addedge(from, to, w);
addedge(to, from, w);
遍历结点 s 的邻接点
对于带权图
cout << "s 的每条出边信息为:" << endl;
int to, w;
for (int i = head[s]; i != -1; i = edge[i].nxt) {
to = edge[i].to;
w = edge[i].w;
cout << "终点:" << to << " 边权:" << w << endl;
}
vector 模拟邻接表
数据结构
用一个大小为 \(n\) 的 vector 数组 \(edge[N]\) 来维护每个结点的出边信息。
\(edge[i]\) 保存以 \(i\) 为起点的每条出边的信息(终点和权值),其中每个元素为一个 pair,如果需要更多信息,可以定义结构体。
对于带权图
struct EDGE {
int to, w; // 边的终点to,边权w
};
vector<EDGE> edge[N]; // edge[i]存储i的所有出边信息
或
typedef pair<int, int> pii;
vector<pii> edge[N]; // 同上,只是用pair实现
对于无权图
vector<int> edge[N]; // edge[i]存储i的所有邻接点
初始化
对于单组测试数据,直接定义全局变量即可,对于多组测试数据,每次要清空 vector。
for (int i = 1; i <= n; i++) { //n 为图中结点个数
edge[i].clear();
}
加边
对于无权有向图,加入一条边 \((from, to)\)
edge[from].push_back(to);
对于无权无向图,加入一条边 \((from, to)\)
edge[from].push_back(to);
edge[to].push_back(from);
对于带权有向图,加入一条边 \((from, to, w)\)
edge[from].push_back(Node{to, w}); // 结构体实现
edge[from].push_back(make_pair(to, w)); // pair实现
或
// c++11加入了emplace_back()函数,可以略微提升时间和空间的效率
edge[from].emplace_back(Node{to, w}); // 结构体实现
edge[from].emplace_back(make_pair(to, w)); // pair实现
对于带权无向图,加入一条边 \((from, to, w)\)
// 结构体实现
edge[from].push_back(Node{to, w});
edge[to].push_back(Node{from, w});
// pair实现
edge[from].push_back(make_pair(to, w));
edge[to].push_back(make_pair(from, w));
或
// c++11加入了emplace_back()函数,可以略微提升时间和空间的效率
// 结构体实现
edge[from].emplace_back(Node{to, w});
edge[to].emplace_back(Node{from, w});
// pair实现
edge[from].emplace_back(make_pair(to, w));
edge[to].emplace_back(make_pair(from, w));
遍历结点 s 的邻接点
对于无权图
printf("s 的邻接点:");
for (const auto &to : edge[s]) {
printf("%d ", to);
}
对于带权图
// 结构体实现
int to, w;
printf("s 的邻接点:");
for (const auto &e : edge[s]) {
to = e.to;
w = e.w;
printf("结点:%d 边权:%d\n", to, w);
}
或
// pair实现
int to, w;
printf("s 的邻接点:");
for (const auto &e : edge[s]) {
to = e.first;
w = e.second;
printf("结点:%d 边权:%d\n", to, w);
}