Tarjan算法

强连通分量

在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;
const int MAXN=10010;
int dfn[MAXN],low[MAXN],Stack[MAXN];
vector<int> graph[MAXN];
//遍历深度以及栈顶指针(指向最后一个元素)
int deep=0,top=-1;
//标记元素是否在栈中
int tag[MAXN];
//强连通分量个数
int cnt=0;
//为不同的强连通分量上色区分
int color[MAXN];

void tarjan(int u){
    dfn[u]=low[u]=++deep;
    Stack[++top]=u;
    tag[u]=1;
    for(int i=0;i<graph[u].size();i++){
        int v=graph[u][i];

        //if(v==fa) continue;统计强连通分量个数不需要这个

        if(dfn[v]==0){

            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        //在栈中才进行更新
        else if(tag[v]==1){
            low[u]=min(low[u],dfn[v]);
        }

    }
    //cout<<u<<":"<<dfn[u]<<" "<<low[u]<<endl;
    //已经出现了一个强连通分量
    if(dfn[u]==low[u]){
        cnt++;
        //将同一个连通分量中的结点全部出栈
        while(tag[u]==1){
            tag[Stack[top]]=0;//置标记
            color[Stack[top]]=cnt;
            top--;
        }
    }
}


int main(){
    int n,m;
    cin>>n>>m;
    memset(dfn,0,sizeof(dfn));
    memset(tag,0,sizeof(tag));
    fill(low,low+n+1,INT_MAX);
    for(int i=0;i<m;i++){
        int from,to;
        cin>>from>>to;
        graph[from].push_back(to);
    }
    tarjan(1);
    cout<<cnt<<endl;
    for(int i=1;i<=n;i++){
        cout<<i<<" color "<<color[i]<<endl;
    }
}
//测试用例
/*
6 8
1 2
1 3
2 4
3 4
3 5
4 1
4 6
5 6
*/



*/



割点

在这里插入图片描述

思路一:依次删除每个割点,然后DFS
思路二:Tarjan算法

#include<bits/stdc++.h>

using namespace std;
const int MAXN=10010;
int deep=0,child=0,root=1,cnt=0;
vector<int> graph[MAXN];
int dfn[MAXN],low[MAXN],tag[MAXN];
vector<int> ans;
/*
访问过
  当前是根结点
  当前不是根结点 且 通过儿子能访问到更早访问过的结点

没访问过且此边不指向父亲节点
*/

//low找到更早的结点一定是不能违背dfs的遍历顺序的
//比如你已经访问过的一条边肯定是不能逆向访问的

//fa为当前结点的父节点
void tarjan(int u,int fa){

    dfn[u]=low[u]=++deep;
    for(int i=0;i<graph[u].size();i++){
        int v=graph[u][i];
        if(v==fa) continue;
        if(dfn[v]==0){//没有遍历过
            tarjan(v,u);
            low[u]=min(low[u],low[v]);//转换1
            //对于根结点是否为割点的判定,记录子树个数
            if(u==root) child++;
            //其他结点u若符合该条件,u就是割点
            //这里改为low[v]>dfn[u] ,则(u,v)是一条割边
            else if(dfn[u]<=low[v]){//理解:不能访问到比自己更先遍历的结点,等于则是刚好成环的那种情况
                tag[u]=1;
            }
        }
        else if{//遍历过且不是指向父亲
            low[u]=min(low[u],dfn[v]);
        }
    }
}

int main(){
    int n,m;
    cin>>n>>m;
    memset(dfn,0,sizeof(dfn));
    fill(low,low+n,INT_MAX);
    memset(tag,0,sizeof(tag));
    for(int i=0;i<m;i++){
        int from,to;
        cin>>from>>to;
        graph[from].push_back(to);
        graph[to].push_back(from);
    }
    tarjan(root,0);
    if(child>=2) tag[root]=1;

    for(int i=0;i<n;i++){
        if(tag[i]){
            cnt++; ans.push_back(i);
        }
    }
    cout<<cnt<<endl;
    for(int i=0;i<ans.size();i++){
        cout<<ans[i]<<" ";
    }
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LCA(最近公共祖先)是指在一棵树,找到两个节点的最近的共同祖先节点。而Tarjan算法是一种用于求解强连通分量算法,通常应用于有向图。它基于深度优先搜索(DFS)的思想,通过遍历图的节点来构建强连通分量Tarjan算法也可以用于求解LCA问题,在有向无环图(DAG)。 具体来说,在使用Tarjan算法求解LCA时,我们需要进行两次DFS遍历。首先,我们从根节点开始,遍历每个节点,并记录每个节点的深度(即从根节点到该节点的路径长度)。然后,我们再进行一次DFS遍历,但这次我们在遍历的过程,同时进行LCA的查找。对于每个查询,我们将两个待查询节点放入一个查询列表,并在遍历过程记录每个节点的祖先节点。 在遍历的过程,我们会遇到以下几种情况: 1. 如果当前节点已被访问过,说明已经找到了该节点的祖先节点,我们可以更新该节点及其所有后代节点的祖先节点。 2. 如果当前节点未被访问过,我们将其标记为已访问,并将其加入到查询列表。 3. 如果当前节点有子节点,我们继续递归遍历子节点。 最终,对于每个查询,我们可以通过查询列表的两个节点的最近公共祖先节点来求解LCA。 需要注意的是,Tarjan算法的时间复杂度为O(V+E),其V为节点数,E为边数。因此,对于大规模的树结构,Tarjan算法是一种高效的求解LCA问题的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值