(简要笔记,仅供参考,可能存在瑕疵或者不正确的地方,望指正)
(1)基于dfs。
(2)用于求解有向图强连通分量的线性时间。
强连通:有向图的两点a和b,a能到b,b能到a。
强连通分量:有向图的子图(有向图的一部分),上面任意两点都能互相到达。
1组成
(1)int dfn[N]//在搜索中到达该点的顺序
(2)int low[N]//最小子树的根,也是可以到达的点中dfn[i]的最小值。
(3)stack<int>vv//不断加入点,当里面的点组成了强连通分量时,就从顶部将这些点删掉。
(4)bool vis[N]//判断该点是否入栈。
(5)int num;//用于给dfn[i],low[i]。
(6)图的存储,这里用链式前向星
struct node{
int to;//终点
int next;//上一条边的编号。
}edge[N];
int head[N];//每个点遍历的起点。
int tot;//编号
void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
tarjan与强连通分量(有向图)
void tarjan(int x)
{
low[x]=dfn[x]=++num;//初始化
vv.push(x);
vis[x]=ture;//将x点压入栈中。
for(i=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])//如果to没有搜索过,那么就向下访问
{
tarjan(to);
low[x]=min(low[x],low[to])//to深搜回来维护low[x];
}
else if(vis[to])//如果已经在栈中,说明,to和x强连通,两者在同一个强连通分量中。
low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x])//x为一个强连通分量的根节点。
{
while(vv.top()!=x)
{
vis[vv.top()]=false;
vv.pop();
}
vis[vv.top()]=false;
vv.pop();//将这个强连通分量所有点从栈中弹出。
}
return;
}
tarjan与有向图缩点
和上面差不多
int color[N];//color[i]表示i点属于哪个强连通分量,把强连通分量看成了一个点。
int count;//记录强连通分量数量,并且作为编号染色。
void tarjan(int x)
{
low[x]=dfn[x]=++num;//初始化
vv.push(x);
vis[x]=ture;//将x点压入栈中。
for(i=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])//如果to没有搜索过,那么就向下访问
{
tarjan(to);
low[x]=min(low[x],low[to])//to深搜回来维护low[x];
}
else if(vis[to])//如果已经在栈中,说明,to和x强连通,两者在同一个强连通分量中。
low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x])//x为一个强连通分量的根节点。
{
count++;
while(vv.top()!=x)
{
color[vv.top()]=count;
vis[vv.top()]=false;
vv.pop();
}
color[vv.top()]=count;
vis[vv.top()]=false;
vv.pop();//将这个强连通分量所有点从栈中弹出。
}
return;
}
遍历
for(i=1;i<=n;i++)
if(!dfn(i))
tarjan(i);
tarjan与割边判定(无向图)
//无向图中链式前项星的tot有一部分(比如割边,边联通分量)从2开始
int tot=1;
bool birdge[2*N]//用于标记割边
void tarjan(int x,int in_edge)//节点x,以及到达x的路径in_edge
{
dfn[x]=low[x]=++num;
for(int i=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])//如果to没有访问过,那就dfs
{
tarjan(to,i);//通过i到to点。
low[x]=min(low[x],low[to]);
if(low[to]>dfn[x]])//割边判定
birdge[i]=birdge[i^1]=ture;//有向边i和它的反向边i^1构成了割边。
}
else if(i!=(in_edge^1))//如果不是从in_edge的反向边回到x的,那么就有另外的边
{
low[x]=min(low[x],dfn[to]);
}
}
}
tarjan与割点判定(无向图)
int root;记录搜索的起点
bool cut[N];//记录是否为割点
void tarjan(int x)
{
dfn[x]=low[x]=++num;
int flag=0;//记录该点连接了几个子树
for(int i=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])
{
tarjan[to];
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])//割点判定
{
flag++;
if(x!=root||flag>1)//如果x不是搜索的点或者是多个子树的交点
cut[x]=ture;//x为割点
}
}
}
else
low[x]=min(low[x],dfn[y]);
}
//遍历
for(int i=1;i<=n;i++)
if(!dfn[i])
{
root=i;
tarjan(i);
}
tarjan与边双联通分量
int color[N];//标记每个点所属的联通分量
int dcc;//标记联通分量数量和编号。
void dfs(int x)
{
color[x]=dcc;
for(int x=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(color[to]||birdge[i])//如果编号为i的边是割边或者color已经标记过了
continue;
dfs(y);
}
}
//遍历
int main(){
for(int i=1;i<=n;i++)
{
++dcc;
dfs(i);
}
}
tarjan与e-DCC缩点
//把边两个联通分量看成两个节点,这里用链式前向星存储
node edge_c[2*N];
int headc[N];
int tc=1;//记录边的数量以及编号。
void add_c(int x,int y)
{
edge_c[++tc]=y;
edge_c[tc].next=headc[x];
head[x]=tc;
}
//遍历,可能会构造出重边
for(int i=2;i<=tot;i++)
{
int x=edge[i^1].to,y=edge[i].to;//由一条无向边相连的两个点。
if(color[x]==color[y])//如果两个点属于同一个子集,无事发生。
continue;
add_c(color[x],color[y]);//建边
}
tarjan与点双联通分量
int root;//遍历搜索的起点。
stack<int>vv;//用栈维护点联通分量.
vector<int>dcc[N];//记录点联通分量。
int cnt;//记录点联通分量的数量和编号
void tarjan(int x)
{
dfn[x]=low[x]=++num;
vv.push(x);//将放入栈内
if(x==root&&head[x]==0)//如果x为搜索起点并且没有边和他相连
{
dcc[++cnt].push_back(x);//将x放入编号为cnt+1的点联通分量中
return;
}
int flag=0;//记录节点连接的子树的数量
for(int i=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])
{
tarjan(to);
low[x]=min(low[x],low[to]);
if(low[to]>=dfn[x])
{
flag++;
if(x!=root||flag>1)//如果不是搜索起点或者x连了多个子树
cut[x]=ture;//x为割点
cnt++;
int z;
do
{
z=vv.top();
dcc[cnt].push_back(z);
vv.pop();
}while(z!=y)
dcc[cnt].push_back(x);
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
//遍历
for(int i=1;i<=n;i++)
if(!dfn[i])
{
root=i;
tarjan(i);
}
tarjan与v-DCC缩点
num=cnt;//把割点看成新的节点,从cnt+1开始。
int new_id[N];//记录节点编号
for(int i=1;i<=n;i++)
{
if(cut[i])
{
new_id[i]=++num;
}
}
int tc=1;
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<dcc[i].size();j++)
{
int x=dcc[i][j];
if(cut[x])
{
add_c(i,new_id[x]);
add_c(new_id[x],i);
}
else
color[x]=i;
}
}