AcWing 847. 图中点的层次| 学习图存储
题目描述
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环。
所有边的长度都是 1,点的编号为 1∼n。
请你求出 1 号点到 n 号点的最短距离,如果从 1 号点无法走到 n 号点,输出 −1。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 a 和 b,表示存在一条从 a 走到 b 的长度为 1 的边。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
数据范围
1≤n,m≤10^5
输入样例:
4 5 1 2 2 3 3 4 1 3 1 4
输出样例:
1
图的链表存储+ bfs算法求解
分析
图的链表存储
使用数组模拟单链表实现
const int N = 100010;
int h[N], e[N], ne[N], idx = 0; // n个单链表,用来存每个点能够到达的其他顶点
void init()
{
memset(h, -1, sizeof h); // 初始所有表头指向-1
}
// 在顶点a和b之间建立一条边
// 头插法
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
int main()
{
// 遍历一个点能到的其他点
// a点能到的其他点
for(int i = h[a]; i != -1; i = ne[i])
{
int t = e[i];
// t就是与a邻接的点
}
}
使用vector实现
const int N = 100010;
int n, m;
vector<int> G[N]; // 存图
// 在顶点a和b之间建立一条边
// 头插法
void add(int a, int b)
{
G[a].push_back(b);
}
int main()
{
// 遍历一个点能到的其他点
// a点能到的其他点
for(auto p : G[a])
{
int t = p;
// t就是与a邻接的点
}
}
图的邻接矩阵存储
就是使用一个二维数组来存边的信息,比较浪费空间
代码
使用数组模拟链表
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N = 100010;
int h[N], e[N], ne[N], idx = 0; // 单链表存图
int d[N]; // 表示到某个点的最短距离
int st[N];
int n, m;
// a,b之间建立一条边
void add(int a, int b)
{
// idx 表示当前这个节点的编号
e[idx] = b; // 创造一个节点存b的值
ne[idx] = h[a]; // 头插
h[a] = idx; // 头插
idx++;
}
int bfs()
{
queue<int> q;
d[1] = 0;
st[1] = true;
q.push(1);
while(q.size())
{
int t = q.front();
q.pop();
// 该点的距离
int dis = d[t];
// 找到了终点
if(t == n) return dis;
// 遍历t所有相邻的节点
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i]; // j表示该点的值
if(!st[j])
{
st[j] = true; // 该点访问过了,其最小距离已经求出
q.push(j); // 把这个点放入队列
d[j] = dis + 1;
}
}
}
return -1;
}
int main()
{
memset(h, -1, sizeof h); // 初始化所有的链表头
scanf("%d%d", &n, &m);
while(m--)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b); // 创建一条边
}
// 使用bfs第一次发现的点的距离,就是到该点的最短距离
cout << bfs() << endl;
return 0;
}
使用vector
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int N = 100010;
int n, m;
vector<int> G[N]; // 存图
int d[N];
int bfs()
{
memset(d, -1, sizeof d); // 初始化距离d
queue<int> q;
d[1] = 0;
q.push(1);
while(q.size())
{
int t = q.front();
q.pop();
if(t == n) return d[t];
// 枚举所有t邻接的点
for(auto point : G[t])
{
if(d[point] == -1)
{
q.push(point);
d[point] = d[t] + 1;
}
}
}
return -1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
G[a].push_back(b); // 有向图
}
cout << bfs() << endl;
return 0;
}
时间复杂度
参考文章
https://www.acwing.com/activity/content/code/content/47104/
https://www.cnblogs.com/moomcake/p/9774402.html
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#define N 10000
using namespace std;
struct EDGE
{
int to;//终点
int cost;//边的权值
};
vector<EDGE>G[N];//G[i]中i表示出发点
int m,n;
int temp1;//出发点
int main()
{
scanf("%d%d",&n,&m);
while(m--)
{
EDGE e;
scanf("%d%d%d",&temp1,&e.to,&e.cost);//输入出发点,终点,边的权值
G[temp1].push_back(e);//将数据压入动态数组,表示在这个出发点下引出的边
//相当于二维动态数组
}
for (int i=1; i<=n; i++) //按照出发点的顺序遍历
{
for(int j=0; j<G[i].size(); j++) //遍历出发点所引出的边
{
EDGE e=G[i][j];//1以二维数组形式输出
printf("from %d to %d,the cost is %d\n",i,e.to,e.cost);
}
}
return 0;
}