基环树找环

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<cmath>
using namespace std;

const int maxn = 500050;

int n,m,vs = 0,cnt = 0,tot = 0,a,b;
int loop[maxn],vis[maxn],f[maxn],Next[maxn],head[maxn],ver[maxn];

inline void add(int x,int y){
    ver[++tot] = y; Next[tot] = head[x]; head[x] = tot; 
}
inline int get(int x){
    if(f[x] == x) return x;
    return f[x] = get(f[x]);
}
inline void getloop(int x){
//    这时我必须要解释一下,这个算法事先进行一次遍历全图的操作,并且将每一个点的f数组都标记为遍历时他的上一个节点(也就是邻接表的起点)当遍历所有子树时,
//     环上的点也差不多遍历完了,当遍历完环上的点时(此时遍历的点为环上连接起点的点(也就是起点倒着数一个的点)),这时再遍历,就会开始递归的return ;
//     从而从这个点开始记录,一直到记录完整个环; 
    vis[x] = ++vs;//一个时间戳,如果这个数组对应为0,意味着未被遍历到; 
    for(int i = head[x]; i ;i = Next[i]){
        int y = ver[i]; //正常思路,记录另一个端点 
        if(y == f[x]){// 1"当遍历完环上的点时,这时再遍历,就会开始递归的return"        的触发点,也是遍历到子树的叶节点后向返回到树根的触发点;因为“
//        每一个点的f数组都标记为遍历时他的上一个节点”,所以如果这条边i的结束点为这个节点的上一个节点,那么就是已经到了尽头了,这时将会continue,
//        而这时continue由于后面没有内容了;将会进行return 
            continue;    
        } 
        if(vis[y] != 0){// 2如果这个点(y)已经遍历过,那么进入这里; 注:由于这个点是过了上一个if的,所以它并不是叶节点; 
            if(vis[y] < vis[x])     continue;    //2如果y点(y,这时的y为环的起点)已被遍历过了,而且是在x之前遍历的,这时就已经找到环了(关于y为环的起点)
            //这是由于x点已经是遍历过所有子树(如果未遍历过所有子树,上一个if将不会放这个点进来) 
//而这个y就是起点 这时进行一次continue操作,同样会引起return;之后环上的点会反复触发这个if从而一直向前倒;直到起点; 而以下的操作都是关于起点的; 
            loop[++cnt] = y;//3上面的那个if如果不触发,就意味着,y点在x点之后遍历这时已经倒回到了环的起点啊,那么,y就是环上的下一个点,(因为建立了双向边)
            //所以这时这条以x为起点,y点为终点的边尚未便利,而且由于连接着起点和起点下一个点的边已被遍历过,那么y点就是起点倒着数一个的点;这时记录y; 
            for(; y!= x; y = f[y]){//并且以y为起点进行倒回,一边倒一边记录,直到起点; 
                loop[++cnt] = f[y];
            }
        }
        else //1如果vis == 0 ,意味着这个点尚未被遍历到, 那么,对它的f数组进行更新,并且以她为起点便利; 
        {
            f[y] = x,getloop(y);    
        }
    //    cout << "????????????"<<x<<endl;
    }
    return; 
}


int main(){
    cin >>n>>m;
    for(int i = 1;i<= m;i++){
        cin >> a>>b;
        add(a,b); add(b,a);
    }
    for(int i= 1;i <= n;i++) f[i] = i;
    getloop(0);
    for(int i = 1;i <= cnt ;i++){
        cout << loop[i] <<endl;
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值