[AcWing]847. 图中点的层次(C++实现)树与图的bfs模板题
1. 题目
2. 读题(需要重点注意的东西)
思路:用广度优先搜索的思想,每当队列pop出一个元素时,将于其距离为1的节点都加到队列中(即层次遍历的思想)。
如果对层次遍历的思想有疑问的同学,建议先划到下方,4. 可能有帮助的前置习题,在给出的链接中复习一下层次遍历的思想。
本题最大的难点就在于如何进行层次遍历,我们来看这样一个例子,有这样一幅图,我已经将其转换成了邻接表进行存储。(不知道如何转换成邻接表存储的同学,建议先划到下方,4. 可能有帮助的前置习题,在给出的链接中复习一下如何用邻接表存储图)
我们可以看到,图中的数组是一个存储头结点的数组,我们给定一个节点1,那么在h[1]指向的这条链表上,都是与节点1相邻的节点(即距离为1);因此,在pop出一个节点t时,只需使用h[t]指向它的链表,再通过for(int i = h[t]; i != -1; i = ne[i]),就可以遍历一整条链表上的节点。然后在遍历时将其加到队列中,并将其的长度置位h[t]+1即可;
上述文字转换成代码如下(d数组用来存储节点1到节点j的距离):
for (int i = h[t]; i != -1; i = ne[i]) // ne[i]上的点都是与i节点距离为1的点
{
int j = e[i]; // 向外走一步
if (d[j] == -1) // 如果j没有被遍历过
{
d[j] = d[t] + 1; // 因为路径长度都是1,所以直接在上一步的基础上加上1即可
q.push(j); // 将j加到队列中
}
}
3. 解法
---------------------------------------------------解法---------------------------------------------------
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int d[N];// 保存1号点到各个点的距离
// -------------------------------A1开始------------------------------------
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
// -------------------------------A1结束------------------------------------
int bfs()
{
//---------------------------------------------A2开始-------------------------------
memset(d, -1, sizeof d);
queue<int> q;
d[1] = 0;
q.push(1);
while (q.size())
{
int t = q.front();
q.pop();
//---------------------------------------A2结束--------------------------------
// ---------------核心代码开始---------------------------------------
// 循环遍历所有与t相距为1的节点
for (int i = h[t]; i != -1; i = ne[i]) // ne[i]上的点都是与i节点距离为1的点
{
int j = e[i]; // 向外走一步
if (d[j] == -1) // 如果j没有被遍历过
{
d[j] = d[t] + 1; // 因为路径长度都是1,所以直接在上一步的基础上加上1即可
q.push(j); // 将j加到队列中
}
}
}
return d[n]; // 返回的d[n]即是节点1到节点n的距离
// ---------------核心代码结束---------------------------------------
}
// ---------------A3开始---------------------------------------
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
cout << bfs() << endl;
return 0;
}
// ---------------A3结束---------------------------------------
一些注释
我将代码分为了A1、A2、A3和核心代码四块;
- 真正基于本题实现的代码,只有核心代码的6句
- A1和A3代码来自于[AcWing]846. 树的重心(C++实现)树与图的dfs模板题具体指的是邻接表的存储和main函数的输入输出;
- A2块代码来自于 [AcWing]844. 走迷宫(C++实现)bfs的思想 ,其展现了层次遍历的思想以及队列是如何实现该思想的。
4. 可能有帮助的前置习题
- [AcWing]844. 走迷宫(C++实现)bfs的思想 (主要是其中的层次遍历的思想以及其是如何用队列实现的)
- 邻接表的存储和main函数的输入输出详见[AcWing]846. 树的重心(C++实现)树与图的dfs模板题
5. 所用到的数据结构与算法思想
- bfs
- 队列
6. 总结
bfs遍历树和图的模板题,主要是层次遍历的思想,其思想简而言之就是当每次pop出队时,将与它距离为1的节点全部加到队列中。推荐熟记全部代码。