【图论】建图、图(含树)的存储与遍历
学习资料
1.B02 图的存储
2.图是如何存储的:BFS、DFS
3.算法讲解059【必备】建图、链式前向星
各种存图方式的区别
图片来自上述学习资料

这里主要推荐表格中的后两种存图方法,因为适用于各种图。
只有在需要对一个点的所有出边进行排序的场合下,使用vector存边排序更方便,其余情况vector存边与链式前向星功能一样。
vector邻接表存图(方便排序)
尤其适用于需要对一个点的所有出边进行排序的场合
模版如下
const int N = 节点数量; //vector邻接表存图不用预先定义边数M,因为vector会自动增长
vector<int> g[N]; //邻接表存图
int st[N]; //标记数组
void add(int a, int b) //添加一条边a->b
{
g[a].push_back(b);
}
//多组测试数据清空邻接表写法
for (auto& x : g) x.clear();//清空邻接表
//for (vector<int>& x : g) x.clear();//auto也可显示写出
memset(st, 0, sizeof st);//清空标记数组
DFS遍历(vector邻接表)
//写法一
void dfs(int x)
{
st[x] = 1;
cout << x << ' ';
for (int i = 0; i < g[x].size(); i++)
{
int y = g[x][i];//编号为x的结点第i条边通向编号为y的结点
if (!st[y])
{
dfs(y);
}
}
}
//写法二
void dfs(int x)
{
st[x] = 1; //标记编号为x的结点已经访问过
cout << x << ' ';
for (int &y : g[x]) //遍历点x所有出边所连的结点,y为结点编号
{
if (st[y]) continue;
dfs(y);
}
}
BFS遍历(vector邻接表)
//写法一
void bfs(int sx)
{
queue<int> q;
q.push(sx);
st[sx] = 1;
while (q.size())
{
int x = q.front();
q.pop();
cout << x << ' ';
for (int i = 0; i < g[x].size(); i++)
{
int y = g[x][i];
if (!st[y])
{
st[y] = 1;
q.push(y);
}
}
}
}
//写法二
void bfs(int sx)
{
queue<int> q;
q.push(sx);
st[sx] = 1;
while (q.size())
{
int x = q.front();
q.pop();
cout << x << ' ';
for (int &y : g[x]) //遍历点t所有出边所连的结点,y为结点编号
{
if (st[y]) continue;
st[y] = 1;
q.push(y);
}
}
}
邻接表存图 例题1 P5318 【深基18.例3】查找文献
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n, m;
vector<int> e[N];
int st[N];
void add(int a, int b)
{
e[a].push_back(b);
}
void dfs(int u)
{
st[u] = 1;
cout << u << ' ';
for (int i = 0; i < e[u].size(); i++)
{
int j = e[u][i];
if (!st[j])
{
dfs(j);
}
}
}
void bfs(int sx)
{
queue<int> q;
q.push(sx);
st[sx] = 1;
while (q.size())
{
int t = q.front();
q.pop();
cout << t << ' ';
for (int i = 0; i < e[t].size(); i++)
{
int j = e[t][i];
if (!st[j])
{
st[j] = 1;
q.push(j);
}
}
}
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
while (m--)
{
int a, b;
cin >> a >> b;
add(a, b);
}
for (int i = 1; i <= n; i++) sort(e[i].begin(), e[i].end());
dfs(1);
cout << '\n';
memset(st, 0, sizeof st);
bfs(1);
return 0;
}
邻接表存图 例题2 P3915 树的分解
// Problem: P3915 树的分解
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3915
// Memory Limit: 125 MB
// Time Limit: 1000 ms
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
using i64 = long long;
using PII = pair<int, int>;
const int N = 1e5 + 10;
int n, k, res;
vector<int> g[N];
int cnt[N], st[N];
void add(int a, int b)
{
g[a].push_back(b);
}
void dfs(int x)
{
st[x] = 1;
cnt[x] = 1;
for (int& y : g[x])
{
if (!st[y])
{
dfs(y);
cnt[x] += cnt[y];
}
}
if (cnt[x] == k)
{
cnt[x] = 0;
res++;
}
}
void solve()
{
for (auto& x : g) x.clear();
memset(cnt, 0, sizeof cnt);
memset(st, 0, sizeof st);
res = 0;
cin >> n >> k;
for (int i = 0; i < n - 1; i++)
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
if (n % k) cout << "NO" << '\n';
else
{
dfs(1);
cout << (res == n / k ? "YES" : "NO") << '\n';
}
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) solve();
return 0;
}
链式前向星存图(不能排序)
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;
// 添加一条边a->b
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 初始化
idx = 0;
memset(h, -1, sizeof h);
DFS遍历板子(链式前向星)
int dfs(int x)
{
st[x] = true; // st[u] 表示点u已经被遍历过
cout << x << ' ';//输出DFS序列
for (int i = h[x]; i != -1; i = ne[i])
{
int y = e[i];
if (!st[y]) dfs(y);
}
}
BFS遍历板子(链式前向星)
void bfs(int sx)//sx为起点
{
queue<int> q;
q.push(sx);
st[sx] = 1;
while (q.size())
{
int x = q.front();
q.pop();
cout << x << ' ';
for (int i = h[x]; i != -1; i = ne[i])
{
int y = e[i];
if (!st[y])
{
st[y] = 1;
q.push(y);
}
}
}
}

浙公网安备 33010602011771号