kosaraju算法用来求有向图的强连通分量
我们需要提前知道的知识
什么是强连通
有向图中,从v可以到达w, 从w也可以到达v,那么称v和w是强连通的
如果一副图中任意两点强连通,这幅图也是强连通的。
强连通分量
- 本图强连通分量为3
- 本图强连通分量为4
- 本图强连通分量为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算法
步骤
- 将图反向
- 得到反向图的逆后序排列
- 用逆后序的顺序遍历原图
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 的道路被封死了。
用这个反向图的逆后序去深度优先搜索(正向)图,可以发现每次递归都是在同一个强连通分量之中。
-
如图
逆后序得到:0 1 4 5 2 3 -
.
逆后序得到 4 5 2 3 0 1 -
.
逆后序得到 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;
}
}
参考自:
- 知乎:https://www.zhihu.com/question/58926821
- 算法第四版(橙色)