使用BFS求最短路
在进行图的学习的时候大家都有学过最短路问题,其中Dijkstra算法和Floyd算法是最经典的了。可是这两个算法在时间复杂度上有着一定的缺陷,单源最短路Dijkstra算法的时间复杂度是O(n2),任意两点最短路Floyd算法的时间复杂度是O(n3),在图比较小的时候这些算法是可以满足要求的。但是也会有其他的一些比较特殊的情况,比如说所有边权都是1的单源最短路应该如何求解呢?这当然可以使用Dijkstra来求解,但是如上所述,时间复杂度难以令人满意。如果大家能回忆起BFS的特性,那么从原点扩散到终点的层数就是源点到终点的最短路。
whuoj使用BFS求最短路问题6. Language of Animals
问题描述:
有n个点,m条无向边,总共有k次查询,每次查询需要返回两个点的最少的中间节点个数,如果这两个点不能相互到达,那么输出-1 。n的规模是2e5,m的规模是3e5,k<=20 。
这个问题很明显就是最短路问题,但是数据规模非常大,如果使用Dijkstra极可能会t。问题中只需要求两个节点的中间节点个数,不存在边权,很容易联想到BFS求最短路。此外还有一个问题是图的存储问题,如果使用邻接矩阵,那么空间复杂度是O(n2),此时达到了4e10的空间大小,如果是int类型,单是图所需要的空间就有16e10B,将近150GB,这肯定是不行的。考虑使用邻接表的话,空间复杂度是O(n+2E),n是顶点数,E是边数,最大规模也不过8e5而已。
#include <iostream>
#include <queue>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int maxn = 2e5+5;
struct anode
{
int no;
anode *next;
};
struct vnode
{
int no;
anode *first;
};
vnode G[maxn];
int bfs(vnode G[],int v,int w,int n){
int dis[maxn]={-1};
int vis[maxn]={0};
int tmp;
anode *p;
queue<int> Q;Q.push(v);vis[v]=1,dis[v]=0;
while(!Q.empty()){
tmp=Q.front();Q.pop();
p=G[tmp].first;
while(p){
if(!vis[p->no]){
Q.push(p->no);vis[p->no]=1;
dis[p->no]=dis[tmp]+1;
}
p=p->next;
}
}
/*for(int i=0;i<n;i++){
cout<<i<<" "<<dis[i]<<endl;
}*/
return dis[w];
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
G[i].first=NULL;
anode *pos;
int t1,t2;
for(int i=0;i<m;i++){
cin>>t1>>t2;
anode *a=(anode *)malloc(sizeof(a));
anode *b=(anode *)malloc(sizeof(b));
a->no=t1,b->no=t2;
a->next=b->next=NULL;
pos=G[t1].first;
if(!pos) G[t1].first=b;
else{
while(pos->next) pos=pos->next;
pos->next=b;
}
pos=G[t2].first;
if(!pos) G[t2].first=a;
else{
while(pos->next) pos=pos->next;
pos->next=a;
}
}
/*for(int i=0;i<n;i++){
anode *p=G[i].first;
cout<<i<<" ";
while(p){
cout<<p->no<<" ";
p=p->next;
}cout<<endl;
}*/
int k;
cin>>k;
for(int i=0;i<k;i++){
cin>>t1>>t2;
if(t1==t2) cout<<"0"<<endl;
else
cout<<bfs(G,t1,t2,n)-1<<endl;
}
return 0;
}
其中每个边节点都有一个由邻接节点构成的链表,这个链表的每次插入都需要申请一个邻接节点的空间。除了结构体和结构体指针对成员的引用不同之外,由于BFS求出来的是最短路,问题要求的是中间节点个数,最后结果还要进行减1 。