书上的这个算法叫做kosaraju算法
- 首先做一次dfs
- 计算图的转置
- 在转置图上按照第一次dfs完成时间f递减的顺序再进行一次dfs
前两个步骤很直白,关键是第3步,要按递减的顺序嘛,于是我就想排序,拍完后发现不对,由于点在g数组的位置发生了变化,整个图的结构完全被打乱了→_→;其实我忽略了dfs非常重要的性质——这不是有一个时间戳嘛?节点探索完成的顺序就是f递增的顺序,放到头插法创建的链表里面就变成了递减的顺序(好吧这其实就是拓扑排序,不过这里的图G可以是有环图)
写这段代码的感受
- 开始写代码之前思路一定要清晰,明白每一步会产生什么后果,如果自己都不明白,那计算机咋办呢?
- 慎用指针,一开始link里面存的不是节点号,是指向图G里面g中某个点的指针,这样一来,dfs2再对link里面的节点进行操作,相当于还是对G而不是Gt进行操作,指来指去的指针呵^-^
typedef struct edge{
int i;//节点号
struct edge *next;
}edge;
typedef struct node{
int i/*节点号*/,d,f,color;
edge *next;
}node;
typedef struct gve{
int v,e;
node *g;/*点的数组*/
}gve;
typedef struct link_element{
int i;/*节点号*/
struct link_element *next;
}link_element;
int insert(node *pnode,int i)
{
edge *new;
new=(edge *)malloc(sizeof(edge));
new->i=i;
new->next=pnode->next;
pnode->next=new;
return 0;
}
int link_insert(link_element **link,int i)/*头插法,使节点按照从头到尾按f递减的顺序排列*/
{
link_element *new;
new=(link_element *)malloc(sizeof(link_element));
new->i=i;
new->next=*link;
*link=new;
return 0;
}
int dfsvisit(gve G,node *pnode,int *time,link_element **link)/*进行第一遍深搜,并产生节点的按f递减排列的链表*/
{
edge *tmp=pnode->next;
(*time)++;
pnode->d=*time;
pnode->color=gray;
while (tmp!=NULL)
{
if (G.g[tmp->i].color==white)
dfsvisit(G,&G.g[tmp->i],time,link);
tmp=tmp->next;
}
pnode->color=black;
(*time)++;
pnode->f=*time;
link_insert(link,pnode->i);/*一完成探索就加入链表*/
return 0;
}
int dfs(gve G,link_element **link)/*进行第一遍深搜,并产生节点的按f递减排列的链表*/
{
int time=0,i;
for (i=1;i<=G.v;i++)
if (G.g[i].color==white)
dfsvisit(G,&G.g[i],&time,link);
return 0;
}
int dfsvisit2(gve G,node *pnode,int *time)/*以某个节点为根深搜,和原版dfsvisit一样*/
{
edge *tmp=pnode->next;
(*time)++;
pnode->d=*time;
pnode->color=gray;
printf("%d ",pnode->i);/*树中每个节点都是根所在强连通分量的节点,根所在强连通分量的节点也都是树中的每个节点,这点在359页有证明*/
while (tmp!=NULL)
{
if (G.g[tmp->i].color==white)
dfsvisit2(G,&G.g[tmp->i],time);
tmp=tmp->next;
}
pnode->color=black;
(*time)++;
pnode->f=*time;
return 0;
}
int dfs2(gve G,link_element **link)/*以第一步产生的link为顺序对转置图进行深搜*/
{
int time=0;
link_element *tmp=*link;
while (tmp!=NULL)
{
if (G.g[tmp->i].color==white)
{
dfsvisit2(G,&G.g[tmp->i],&time);
printf("\n");
}
tmp=tmp->next;
}
return 0;
}
gve trans(gve G)/*计算图的转置,不需要保留原图的d和f,只要把颜色置为white,边反向即可*/
{
gve Gt;
Gt.v=G.v;
Gt.e=G.e;
Gt.g=(node *)malloc(sizeof(node)*(Gt.v+1));
int i;
for (i=1;i<=Gt.v;i++)
{
Gt.g[i].next=NULL;
Gt.g[i].color=white;
Gt.g[i].i=i;
}
edge *tmp;
for (i=1;i<=Gt.v;i++)
{
tmp=G.g[i].next;
while (tmp!=NULL)
{
insert(&Gt.g[tmp->i],i);
tmp=tmp->next;
}
}
return Gt;
}
int css(gve G)/*计算强连通分量*/
{
link_element **link;
link=(link_element **)malloc(sizeof(link_element *));
*link=NULL;
gve Gt;
dfs(G,link);
Gt=trans(G);
dfs2(Gt,link);
return 0;
}
int main(void)
{
int i;
gve G;
scanf("%d%d",&G.v,&G.e);
G.g=(node *)malloc(sizeof(node)*(G.v+1));
for (i=1;i<=G.v;i++)
{
G.g[i].next=NULL;
G.g[i].color=white;
G.g[i].i=i;
}
int s,d;
for (i=1;i<=G.e;i++)
{
scanf("%d%d",&s,&d);
insert(&G.g[s],d);
}
css(G);
return 0;
}