kosaraju算法

kosaraju算法用来求有向图的强连通分量


我们需要提前知道的知识

什么是强连通

有向图中,从v可以到达w, 从w也可以到达v,那么称v和w是强连通的
如果一副图中任意两点强连通,这幅图也是强连通的。


强连通分量

  1. 本图强连通分量为3
    在这里插入图片描述
  2. 本图强连通分量为4
    在这里插入图片描述
  3. 本图强连通分量为4
    在这里插入图片描述

无向图的连通分量怎么计算

kosaraju算法不过是在下面的基础上增加了几行,

package Algorithm;

public class CC {
    private int count;//连通分量个数
    private boolean[] marked;//标记是否访问过
    private int[] id;//id[v]=1,说明顶点v在分量1里面
    public CC(Graph g){
        marked=new boolean[g.V()];
        id=new int[g.V()];
        for (int i = 0; i <g.V(); i++) {
            if(!marked[i]){
                dfs(g,i);
                count++;
            }
        }
    }
    private void dfs(Graph g,int v){
        marked[v]=true;
        id[v]=count;
        for (int w:g.adj(v)){
            if(!marked[w])  dfs(g,w);
        }
    }
    public boolean connected(int v,int w){
        return id[v]==id[w];
    }
    public int count(){
        return count;
    }
    public int id(int v){
        return id[v];
    }
}

什么是顶点的逆后序排列

调用dfs时,将顶点放在栈里面即可。

package Algorithm.DirectedGraph;
import java.util.LinkedList;
public class DF_Order {
    private LinkedList<Integer>  reversePost;//栈
    private boolean[] marked;//标记是否访问过
    private int vNum;//顶点数目
    public DF_Order(DiGraph g){
        vNum=g.getV();
        reversePost=new LinkedList<>();
        marked=new boolean[g.getV()];
        for (int i = 0; i < g.getV(); i++) {
            if(!marked[i]){
                dfs(g,i);
            }
        }
    }
    private void dfs(DiGraph g, int i) {
        marked[i]=true;
        for(int w:g.adj(i)){
            if(!marked[w]){
                dfs(g,w);
            }
        }
        reversePost.push(i);
    }
    public Iterable<Integer> reversePost(){
        return reversePost;
    }
}


什么是反向图

将有向图的边的指向反过来即可。
在这里插入图片描述
反向图:
在这里插入图片描述


kosaraju算法

步骤

  1. 将图反向
  2. 得到反向图的逆后序排列
  3. 用逆后序的顺序遍历原图
package Algorithm.DirectedGraph;

public class KosarajuCC {
    private int[] id;
    private int count;
    private boolean[] marked;
    public KosarajuCC(DiGraph graph){
        marked=new boolean[graph.getV()];
        id=new int[graph.getV()];
        DF_Order order = new DF_Order(graph.reverse());
        for (int x:order.reversePost()){
            if(!marked[x]) {
                dfs(graph,x);
                count++;
            }
        }
    }

    private void dfs(DiGraph graph, int v) {
        marked[v]=true;
        id[v]=count;
        for (int x:graph.adj(v)){
            if(!marked[x]) dfs(graph,x);
        }
    }
    public boolean stronglyConnected(int v,int w){
        return id[v]==id[w];
    }
    public int getCount(){
        return count;
    }

}


对于该算法的理解

我们从哪一个顶点进行DFS呢?
按照顶点顺序:0 1 2 3 4 5
那么0 1是一个强连通分量
2 3 4 5是一个强连通分量,显然是不对的。
按照逆后序就是: 0 1 4 5 2 3
那么0 1是一个强连通分量
4 5是一个强连通分量
2 3是一个强连通分量
先访问0 1 4 5,在进行2 3,那么2通向1或4 的道路被封死了。
在这里插入图片描述

用这个反向图的逆后序去深度优先搜索(正向)图,可以发现每次递归都是在同一个强连通分量之中。

  1. 如图
    在这里插入图片描述
    逆后序得到:0 1 4 5 2 3

  2. .
    在这里插入图片描述
    逆后序得到 4 5 2 3 0 1

  3. .
    在这里插入图片描述
    逆后序得到 2 3 0 1 4 5


无向图,有向图的图结构

package Algorithm.DirectedGraph;

import Algorithm.Bag;

public class DiGraph {

    private int v;
    private int e;
    private Bag<Integer>[] adj;
    public DiGraph(int v,int e){
        this.v=v;
        this.e=e;
        adj=(Bag<Integer>[]) new Bag[v];
        for (int i = 0; i < v; i++) {
            adj[i]=new Bag<Integer>();
        }
    }
    public DiGraph(int v){
        this.v=v;
        adj=(Bag<Integer>[]) new Bag[v];
        for (int i = 0; i < v; i++) {
            adj[i]=new Bag<Integer>();
        }

    }
    public int getV(){return v;};
    public int getE(){return e;};
    public void add(int v,int w){
        adj[v].add(w);
    }
    public Iterable<Integer> adj(int v){
        return adj[v];
    }
    public DiGraph reverse(){
        DiGraph diGraph = new DiGraph(v);
        for (int i = 0; i < v; i++) {
            for (int x:adj(i)){
                diGraph.add(x,i);
            }
        }
        return diGraph;
    }
}


//无向图
package Algorithm;
public class Graph {
    private int V;
    private int E;
    private Bag<Integer>[] adj;//邻接表
    public Graph(int v,int e) {
        this.V = v;
        this.E=e;
        adj=(Bag<Integer>[]) new Bag[v];
        for (int i = 0; i < V; i++) {
            adj[i]=new Bag<Integer>();
        }
    }
    public Graph(int v){
        this.V=v;
        adj=(Bag<Integer>[]) new Bag[v];
        for (int i = 0; i < v; i++) {
            adj[i]=new Bag<Integer>();
        }
    }
    public int V(){
        //返回定点数
        return V;
    }
    public int E(){
        //返回边数
        return E;
    }
    public void addEdge(int v,int w){
        //添加一条边
        adj[v].add(w);
        adj[w].add(v);
        E++;
    }
    public Iterable<Integer> adj(int v){
      //和v相邻的所有顶点。只是输入的时候,不包含计算得出的所有
        return adj[v];
    }
}

package Algorithm;

import java.util.Iterator;

public class Bag<Item> implements Iterable<Item> {
    @Override
    public Iterator<Item> iterator() {//实现iterable,才可以foreach
        //iterator 迭代器
        return new ListIterator();
    }
    private class ListIterator implements Iterator<Item> {
        private node current=first;
        @Override
        public boolean hasNext() {
            return current!=null;
        }

        @Override
        public Item next() {
            Item item=current.val;
            current=current.next;
            return item;
        }
    }
    private class node{
        Item val;
        node next;
    }
    private int n=0;
    private  node first;
    public void add(Item item){
        node oldfirst=first;
        first=new node();
        first.val=item;
        first.next=oldfirst;
        n++;
    }
    public boolean isEmpty(){
        return first==null;
    }
    public int size(){
        return n;
    }
}

参考自:

  1. 知乎:https://www.zhihu.com/question/58926821
  2. 算法第四版(橙色)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值