连通图的强连通分支

来源:互联网 发布:软件集成什么意思 编辑:程序博客网 时间:2024/04/27 22:12
//双连通分量方法一:
//定义一:给定的有向图G=(V,E),图中任意两个顶点u,v都属于V;如果u,v可以
//互相到达,则称G是强连通图。
//定义二:有向图的极大强连通子图称为强连通分支。
//由定义可2以得知有向图的强连通分支是一个最大的顶点集合,在这个集合中
//所有点都可以互相到达。。
//寻找强连通分支的步骤可以归结为:
//1.对图进行深度优先遍历,得到后续遍历顺序号
//2.将图中所有的边全部反转。得到新的图形。
//3.对新图形进行深度优先遍历。进行的次数就是强连通分支的个数。
//模板如下:
struct NODE
{
int v;
struct NODE *next;
};
void dfs(int v,int n,NODE node[],int postn[],int visited[],int count)
{
NODE *p;
visited[v]=true;
p=node[v].next;
while(p!=NULL)
{
if(!visited[p->v])
{
dfs(p->v,n,node,postn,visited,count);
}
p=p->next;
}
postn[v]=++count;
}


void reverse(NODE node[],NODE newnode[],int n)
{
for(int i=0;i<n;i++)newnode[i].next=NULL;//将所有的邻接矩阵初始化为NULL
for(int v=0;v<n;v++)
{
NODE *p=node[v].next;
while(p!=NULL)
{
NODE *p1=new NODE;
p1->v=v;
p1->next=newnode[p->v].next;
newnode[p->v].next=p1;
p=p->next;
}
}
}


int strongly_connect_picture(NODE node[],int n)
{
int *postn[n]=new int[n];
int *postm[n]=new int[n];
bool*visited[n]=new bool [n];
NODE *newnode=new NODE[n];
int count=0;
int num=0;
for(int i=0;i<=n;i++)visited[i]=false;//将所有的点标记为未访问过的
for(int i=0;i<n;i++)//进行第一次遍历
{
if(!visited[i])
dfs(i,n,node,postn,visited,count);
}
for(int i=0;i<n;i++)//将按顶点顺序存放的后续遍历序号转化为按后续遍历序号存放
{ //的顶点序号
postm[postn[i]-1]=i;
}
reverse(node,newnode,n);//将有向图的所有边全都反转
for(int i=n-1;i>=0;i--)
{
if(!visited[postm[i]])
{
sum++;
dfs(postm[i],n,newnode,postn,visited,count);
}
}
delete visited;delete postn;delete postm;delete newnode;
return sum;
}
//下面是我想的一个例子。


#include<iostream>
#include<string.h>
using namespace std;


int map[100][100];
int map1[100][100];
int visited[100];
int postm[100];
int postn[100];
void reverse(int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
map1[i][j]=map[j][i];
}
}
}
void dfs(int v,int n,int count)
{
visited[v]=1;
for(int i=1;i<=n;i++)
{
   if(map[v][i]&&!visited[i])
   {
       dfs(i,n,count);
   }
}
postn[v]=count++;
}
void dfs2(int v,int n,int count)
{
visited[v]=1;
for(int i=1;i<=n;i++)
{
   if(map1[v][i]&&!visited[i])
   {
       visited[i]=1;
       dfs(i,n,count);
   }
}
postn[v]=count++;
}
int strongly_connect_picture(int n)
{
memset(visited,0,sizeof(visited));
int count=0;
for(int i=1;i<=n;i++)
{
if(!visited[i])
{
dfs(i,n,count);
}
}
reverse(n);
for(int i=1;i<=n;i++)
{
   postm[postn[i]]=i;
}
int sum=0;
count=0;
for(int i=n;i>=1;i--)
{
   if(!visited[postm[i]])
   {
       sum++;
       dfs2(i,n,count);
   }
}
return sum;
}
int main()
{
int n;
while(cin>>n,n)
{
int m;
cin>>m;
memset(map,0,sizeof(map));
for(int i=1;i<=n;i++)
map[i][i]=1;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
map[u][v]=1;
}
cout<<strongly_connect_picture(n)<<endl;
}
return 0;
}




//连通分量方法二:
//定义:一个无向图中存在某些点a,一点删除a以及其相关联的边,图不再是连通图
//则a就是关节点。如果无向图不包含任何的关节点,那么就称图是双连通图。一个无向图
//的双连通分量是图的极大双连通子图。
//给定的无向连通图G,S是一棵深度优先树,图中节点a是一个关节点,当且仅当
//1.a是根,并且a至少有两个孩子。
//2.a不是根,并且a的某棵子树上没有指向a的祖先的反向边






//对于一个无向连通图G,下面的说法是等价的:
//1.图G是双连通的。
//2.图G的任意两点之间存在简单回路。
//3.图G中不包含关节点。
//首先我们来求一下关节点
void dfs(int u,int p)//p是u的双亲节点
{
    low[u]=d[u]=time++;
    for(NODE *w=a[u];w;w=w->next)
    {
        int v=w->v;
        if(d[v]==-1)
        {
            dfs(v,u);
            if(low[u]>low[v])
                low[u]=low[v];//回溯求low[u]<u,v>是树边
        }
        else
        {
            if(v!=p&&low[u]>d[v])
                low[u]=d[v];//<u,v>是反向边
        }
    }
}




//利用上述思想我们还可以求双连通分量
int low[10000];
int d[10000];
stack<EDGE>s;
for(int i=1;i<=n;i++)
{
    d[i]=-1;
}
void connect(int u,int p)//p是u的双亲节点
{
    d[u]=low[u]=time++;
    for(Enode*w=a[u];w;w=w->next)
    {
        int v=w->v;
        EDGE e;
        e.u=u;
        e.v=v;
        if(v!=p&&d[u]>d[v])s.push(e);//边进栈
        if(d[v]==-1)
        {
            connect(v,u);
            if(low[u]<=low[v])
            {
                cout<<"New bicoomponent\n";
                do
                {
                    e=s.top();
                    s.pop();
                    if(u<v&&e.u>e.v)swap(e.u,e.v);
                    if(u>v&&e.u<e.v)swap(e.u,e.v);//使得输出结果更加直观
                    cout<<"("<<e.u<<e.v<<")"<<endl;
                }while(e.u!=u||e.v!=v);
            }
            else
            {
                low[u]=low[v];//树边
            }
        }
        else
        {
            if(v!=p&&low[u]>d[v])
                low[u]=d[v];//反向边
        }
    }
}
原创粉丝点击