拓扑排序的DFS和BFS

时间:2023-02-02 14:38:59

博主以前有一个疑问,DFS和BFS各自的适用范围是?我想你今天看了这篇文章之后会有一个判断!


BFS

数据结构与算法分析:c语言描述(p217)
已经存在一个Indgree入度数组(indgree[v]={(u,v)的数目})
以及一个邻接矩阵,求一个拓扑排序
提示:图中出现环就会拓扑失败
代码风格被我改为了C++

void TopSort(vector<vector<int>> G){    
//图中所有的点都要被遍历到,每次取出一个点,共执行NumVertex次
for(int counter=0;counter<G.size();counter++){
int v=findVertexIndgreeZero(G);//寻找入度为0的点
if(!v){
cout<<"有环,失败!"<<endl;
return;
}
TopNum[v]=counter;//第counter+1个被遍历到的
for(int i;i<G[v].size();i++){
if(G[v][i])
indgree[i]--;
}
}
}

用一句话来概括这个算法就是依次寻找出入度为0的点,然后将其所通的所有点入度都减1。循环直至结束或者报错(有环)。

上面这个算法存在优化的余地,findVertexIndgreeZero()的复杂度为O(n),执行n次为O(N2)次,用一个队列会大大缩减复杂度

void TopSort(vector<vector<int>> G){
int counter=0;
queue<int> q;
for(int i=0;i<G.size();i++){
if(indgree[i]==0);
q.push(i);
}
while(!q.empty()){
int u=q.top();q.pop();
TopNum[u]=counter++;
for(int i=0;i<G[u].size();i++){
if(G[u][i]){
if(--indgree[i]==0)
q.push(i);
}
}
}
if(counter!=G.size())
cout<<"有环"<<endl;
}

这个结果很有趣,我们可以不严谨地总结一下“拓扑排序就是在找入度0点+更新入度”。
题外话
另外TopNum[i]保存了遍历的顺序。那么能不能TopNum[counter]=i,这样呢。。也可以。因为i和counter肯定是一一映射的关系。如果学习过数据库,那么你就可以说:谁来做主键都可以。

DFS

刘汝佳 算法竞赛入门经典第二版
P167
假设有n个变量,m个二元组(u,v),分别表示u< v。那么寻找一个不等式,包含所有的变量。类似于a< b,b< c ,则输出a< b< c;

我们可以把二元组小于关系看作边关系。这一转换其实很自然
然后,寻找一个不等式,其实就是在寻找一个拓扑顺序。那么,这个问题其实就是一个拓扑排序,完全可以用BFS完成

但是还有另外一种比较有趣的解法,就是使用dfs

我们首先定义一个函数

bool dfs(int u);//u代表当前点,返回点u之后是否存在一个拓扑路线

有了这个定义就不难继续做下去了,我们再想到:当前点u若是想能够返回true,那么它所能到达(u->v)的所有点也应该都能。
那么,失败的具体条件是什么?就是成环

只要之后遍历到的点和之前的点u有关系(v->u),那么就说明成环!返回false。
那么,代码应该是:

int vector<vector<int>> G 
int c[maxn];
int TopNum[maxn];
int t;
bool dfs(int u){
c[u]=-1;
for(int i=0;i<G.size();i++)
if(G[u][i]){
if(c[i]==-1){//大水冲了龙王庙,这个i点在之前已经被遍历到了,成环!
return false;
}
if(!c[i]&&!dfs(i)) return false;
}
//经过了检验
c[u]=1;
TopNum[--t]=u;
return true;
}
bool topsort(){
int t=n;
memset(c,0.sizeof(c));
for(int i=0;i<G.size();i++)
if(!c[u])
if(!dfs(u))
return false;
return true;
}