#include<bits/stdc++.h> usingnamespace std; constint maxn = 1e+7; int n; int p[maxn]; vector<int> gra[10]; vector<int> res; intinit(){ memset(p,0,sizeof(p)); for(int i = 1; i <= n; i++){ p[i] = i; } } intfind(int x){ return p[x]==x?x:p[x]=find(p[x]); } intunit(int x,int y){ p[x] = y; } voidaddedge(int x,int y){ gra[x].push_back(y); gra[y].push_back(x); } int s,e; int vis[maxn]; int path[maxn]; voiddfs(int x){ cout<<x<<endl; for(int i = 0; i < gra[x].size(); i++){ int to = gra[x][i]; if(!vis[to]){ path[to] = x;// 记录路径 vis[to] = 1; if(to == e){ return; } dfs(to); } } } intmain(){ freopen("in.txt","r",stdin); cin>>n; init(); memset(vis,0,sizeof(vis)); memset(path,0,sizeof(path)); for(int i = 0; i < n; i++){ int a,b; cin>>a>>b; int aa = find(a),bb = find(b); if(aa!=bb){ unit(a,b); }else{// 说明构成了环 s = a,e = b; break; } addedge(a,b); } vis[s] = 1; dfs(s); int temp = e; res.push_back(temp); while(path[temp]){ res.push_back(path[temp]); temp = path[temp]; } sort(res.begin(),res.end()); for(int i = 0; i < res.size(); i++){ if(i>0){ cout<<' '; } cout<<res[i]; } }
另外还可以采用深搜的方式,首先需要了解
在图的遍历中,往往设置了一个标记数组 vis 来记录顶点是否被访问过。但有些时候需要改变 vis 值的意义。令 vis 具有 3 种值并表示 3 种不同含义
vis = 0,表示该顶点没没有被访问 vis = 1,表示该顶点已经被访问,但其子孙后代还没被访问完,也就没从该点返回 vis = 2,,表示该顶点已经被访问,其子孙后代也已经访问完,也已经从该顶点返回 可以 vis 的 3 种值表示的是一种顺序关系和时间关系
《算法导论》334 页有这 4 种边的准确定义
DFS 过程中,对于一条边 u->v vis[v] = 0,说明 v 还没被访问,v 是首次被发现,u->v 是一条树边 vis[v] = 1,说明 v 已经被访问,但其子孙后代还没有被访问完(正在访问中),而 u 又指向 v,说明 u 就是 v 的子孙后代,u->v 是一条后向边,因此后向边又称返祖边, vis[v] = 2,说明 v 已经被访问,其子孙后代也已经全部访问完,u->v 这条边可能是一条横叉边,或者前向边
注意:树边,后向边,前向边,都有祖先,后裔的关系,但横叉边没有,u->v 为横叉边,说明在这棵 DFS 树中,它们不是祖先后裔的关系它们可能是兄弟关系,堂兄弟关系,甚至更远的关系,如果是 dfs 森林的话,u 和 v 甚至可以在不同的树上