一、引言
众所周知,找最短路有很多方法,例如:Dijkstra、Floyd等等,今天用BFS来解最短路
二、重难点
BFS(广度优先搜索)
BFS与DFS不同,用一棵二叉树来理解,DFS便像是前序遍历或后序遍历,BFS就是层序便利
DFS是暴力搜索,到头才返回,而BFS是一层一层遍历
BFS通常解法是用while循环遍历,用队列存储访问过的节点
数组模拟单链表
链表在内存中各个元素的存储并不是连续的,所以需要一个指针去指到下一个元素,数组模拟便
需要用到多个数组n,ne,h,dist,这些数组分别存储边到达的节点编号、该元素下一个元素的
编号、每个节点的祖宗节点(头节点)、1号点到这个点的距离.
acwing上的题目为每条长度恒等于1且求1到n号节点的最短路,所以没有用w数组表示边的长度
那,数组怎么模拟链表的插入节点呢?
这里我们用到头插法,将新增节点插入祖宗节点的下一节点(后继节点的第一个节点)
头插法代码如下:
void add(int a, int b){ //头插法实现添加节点
e[idx] = b; //将边记录下来
ne[idx] = h[a]; //使新增节点的指针指向头节点指针指向的节点
h[a] = idx++; //记录头节点
}
即可完成头插
三、代码
原题:
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环。
所有边的长度都是 1,点的编号为 1∼n。
请你求出 1 号点到 n 号点的最短距离,如果从 1 号点无法走到 n 号点,输出 −1。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 a 和 b,表示存在一条从 a 走到 b 的长度为 1 的边。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
数据范围
1≤n,m≤1e5
输入样例:
4 5 1 2 2 3 3 4 1 3 1 4
输出样例:
1
代码如下:
//数组模拟数
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int e[N]; //边的结尾顶点
int ne[N]; //每个点的下一个点
int h[N]; //每个顶点的头节点
int idx = 1; //点的编号
int dist[N]; //一号节点到这个节点的距离
int n,m;
queue<int> q; //队列实现BFS
void add(int a, int b){ //头插法实现添加节点
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int bfs(){
q.push(1); //先推入一号节点
memset(dist,0x3f,sizeof dist);
dist[1] = 0; //一号节点到自己的距离为0
while(!q.empty()){ //用BFS找最短路
int f = q.front();
q.pop();
for(int i = h[f]; i > 0; i = ne[i]){
if(dist[i] > dist[f] + 1){ //如果还没有遍历到而且可以走到,就走这条路
dist[i] = dist[f] + 1;
q.push(i); //入队继续找下一层
}
}
}
return (dist[n] == 0x3f3f3f3f) ? -1 : dist[n]; //如果没被找到(没有路)就返回-1 否则返回层数(dist[n])
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 0; i < m; i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
int ans = bfs();
printf("%d",ans);
}