Kosaraju算法---求解强连通分量

有向图强连通分量在有向图G中,如果两个顶点Vi,Vj间(Vi>Vj)有一条从Vi到Vj的有向路径,同时还有一条从Vi到Vj的有向路径,则称这两个顶点强。连通(strongly connected),如果有向图G中的任意两个顶点都强连通,则称G是一个强连通图。

如下面的图,虽然不是强连通图,但是有3个强连通分量,都用红色框标注。

Kosaraju算法、Tarjan算法、Gabow算法皆为寻找有向图强连通分量的有效算法。但是在Tarjan 算法和 Gabow 算法的过程中,只需要进行一次的深度优先搜索,而Kosaraju算法需要两次DFS,因而相对 Kosaraju 算法较有效率。这些算法可简称为SSC(strongly connected components)算法;

Kosaraju 算法即为算法导论一书给出的算法,比较直观和易懂。这个算法可以说是最容易理解,最通用的算法,其比较关键的部分是同时应用了原图G和反图GT。 它利用了有向图的这样一个性质,一个图和他的transpose graph(边全部反向)具有相同的强连通分量!

## 算法步骤 ##

1) 创建一个空的栈 ‘S’ ,然后对图做DFS遍历. 在顶点访问完成后加入栈中。访问完成是说回溯返回时,而不是第一次发现该节点时。fillOrder()函数
2) 得到图转置,即将所有边反向。
3) 从S中依次弹出每个顶点,设为 ‘v’. 将v看做遍历的源点 (调用 DFSUtil(v)). 做第二次DFS遍历,可以找到以v为起点的强连通分量,打印出即可。

## 代码 ##
    #include<iostream>
#include<list>
#include<stack>
using namespace  std;


class  Graph
{

    int V;  //顶点个数
    list<int>  *adj;   //邻接表


    void fillOrder(int V, bool visited[], stack<int> &stack);//最晚完成的遍历顶点放在栈顶


    void DFSUtil(int v, bool visited[]);//DFS打印以V为起点的边
public:

    Graph(int V);
    void addEdge(int v, int w);


    void printSCCs();//打印所有的连通分量


    Graph getTranspose();//得到当前图的转置图
};

Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V];
}

void Graph::DFSUtil(int v, bool visited[])
{
    visited[v] = true;
    cout << v << " ";
    list<int>::iterator i;
    for (i = adj[v].begin(); i != adj[v].end();i++)
    {
        if (!visited[*i])
        {
            DFSUtil(*i, visited);
        }
    }
}

Graph Graph::getTranspose()
{
    Graph g(V);
    for (int v = 0; v < V;v++)
    {
        list<int>::iterator i;
        for (i = adj[v].begin(); i != adj[v].end();++i)
        {
            g.adj[*i].push_back(v);
        }
    }

    return g;
}



void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w);
}

void Graph::fillOrder(int v, bool visited[], stack<int> &stack)
{
    visited[v] = true;
    list<int>::iterator i;
    for (i = adj[v].begin(); i != adj[v].end();i++)
    {
        if (!visited[*i])
        {
            fillOrder(*i, visited, stack);
        }
    }
    stack.push(v);
}



void Graph::printSCCs()
{
    stack<int> stack;


    bool *visited = new bool[V];
    for (int i = 0; i < V;i++)
    {
        visited[i] = false;
    }

    for (int i = 0; i < V;i++)
    {
        if (visited[i]==false)
        {
            fillOrder(i, visited, stack);//根据完成时间压入栈中,而栈顶是完成时间最晚的顶点;
        }
    }



    //下面,我们来创建转置图

    Graph gr = getTranspose();



    //准备第二次DFS
    for (int i = 0; i < V;i++)
    {
        visited[i] = false;
    }

    while (stack.empty()==false)
    {
        int v = stack.top();
        stack.pop();

        //打印以v为起点的强连通分量
        if (visited[v]==false)
        {
            gr.DFSUtil(v, visited);
            cout << endl;
        }

    }
}


//测试程序

int main() {

        // 创建图

            Graph g(5);

        g.addEdge(1, 0);

        g.addEdge(0, 2);

        g.addEdge(2, 1);

        g.addEdge(0, 3);

        g.addEdge(3, 4);



        cout << "Following are strongly connected components in given graph \n";

        g.printSCCs();



        return 0;

}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值