Kosaraju的主要步骤:
- 对G求解Reverse Post-Order,即上文中的”伪拓扑排序“
- 对G进行转置得到GR
- 按照第一步得到的集合中顶点出现的顺序,对GR调用DFS得到若干颗搜索树
- 每一颗搜索树就代表了一个强连通分量
package 算法导论;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
public class shiyan4 {
static int len_v=10; //点的个数
static int len_e=20; //边的个数
private static boolean visited2[]=new boolean[len_v]; //DFS中被访问的点
static Vertex ver[]=new Vertex[len_v]; //存放点
static int len[]=new int[len_v];
static int num[]=new int[len_v]; //链表中此时被访问的点
static Stack<Vertex> s=new Stack<Vertex>(); //非递归DFS的栈
static Stack<Integer> sta=new Stack<Integer>(); //存放拓扑排序
static Vertex ver_translate[]=new Vertex[len_v]; //存放点
private static boolean visited_translate[]=new boolean[len_v]; //BFS中被访问的点
static Stack<Integer> translate_stack=new Stack<Integer>(); //非递归DFS的栈
static class Vertex
{
int n;//邻接链表的点
List<Integer> list=new LinkedList<Integer>();
//点的构造方法
public Vertex(int n)
{
this.n=n;
}
//向该点的链表中增加点
public void add(int value)
{
list.add(value);
}
}
static class Edge
{
int u;//该点
int v;//下一个点
//构造方法
public Edge(int u,int v)
{
this.u=u;
this.v=v;
}
@Override
//重写hashcode方法
public int hashCode() {
return v;
}
@Override
//重写equals方法
public boolean equals(Object obj) {
if(!(obj instanceof Edge)){
return false;
}
if(obj == this){
return true;
}
return this.v==((Edge)obj).v&&this.u==((Edge)obj).u;
}
}
//csh即初始化
public static void csh()
{
//构建100个点
for(int i=0;i<len_v;i++)
{
ver[i]=create(i+1);
visited2[i]=false;
}
//构建500条边
Edge e;
Set<Edge> s=new HashSet<Edge>();//存储所有的边
while(s.size()<len_e)
{
int u=random();
int v=random();
while(u==v||s.contains(new Edge(u,v)))
v=random();
e=new Edge(u,v);
if(!s.contains(e))
s.add(e);
}
//将500条边加到对应的起始点的链表中
Iterator<Edge> it=s.iterator();
while(it.hasNext())
{
Edge ee=(Edge) it.next();
ver[ee.u-1].list.add(ee.v);
//System.out.println(ee.u+" * "+ee.v);
}
}
//重新初始化BFS和DFS标注点
public static void csh1()
{
for(int i=0;i<len_v;i++)
{
visited2[i]=false;
}
}
public static void print(Vertex ver[])
{
System.out.println("打印所有点的邻接链表:");
//打印所有的邻接链表
for(int i=0;i<len_v;i++)
{
int len=ver[i].list.size();
System.out.print(ver[i].n+"-->");
for(int j=0;j<len;j++)
System.out.print(ver[i].list.get(j)+"-->");
System.out.println("");
}
}
public static int random()
{
//生成随机数1-100
int i=(int) (Math.random()*len_v+1);
return i;
}
//构造100个点的邻接链表
public static Vertex create(int n)
{
return new Vertex(n);
}
//非递归DFS
public static void DFS2(Vertex v)
{
visited2[v.n-1]=true; //将参数中对应点标为已读
s.add(v); //将初始点加入栈中
while(!s.empty())
{
int i=0; //现在搜寻点邻接链表中的第一个邻接点
while(i<v.list.size())
{
Vertex tem=ver[(int)v.list.get(i)-1]; //定义为该点的下一个点
if(visited2[tem.n-1]==false)
{
s.add(tem); //将该点邻接表中第一个(未读)邻接点放入栈中
i=0;
visited2[tem.n-1]=true; //把刚才放入的点标记为已读
}
else
i++; //若第i个点已读,继续寻找未读邻接点
v=s.peek(); // v=刚才放入的那个点(即继续向下一层进行while寻找)
}
if(!sta.contains(v.n))
sta.push(v.n); //拓扑排序放进栈里
s.pop(); //这一条深度搜寻链已到底,将最底下的那个点从栈中删除
if(!s.empty())
v=s.peek(); //回溯到上一个点
}
}
//反转图
public static void translate(Vertex ver[])
{
for(int i=0;i<len_v;i++)
{
ver_translate[i]=create(i+1);
//ver_translate[i].list.add(i+1);
visited_translate[i]=false;
}
for(int i=0;i<ver.length;i++)
{
int n=ver[i].n;
List<Integer> l=ver[i].list;
for(int j=0;j<l.size();j++)
{
if(!ver_translate[(int)l.get(j)-1].list.contains(n))
ver_translate[(int)l.get(j)-1].list.add(n);
}
}
}
//反转图DFS
public static void translate_DFS(Vertex v)
{
visited_translate[v.n-1]=true; //将参数中对应点标为已读
translate_stack.push(v.n); //将已遍历过的强连通分量的点入栈
System.out.print(v.n+",");
//接下来的操作针对的是该点的邻接链表中的点(除自身)
int i=0;
while(i<v.list.size())
{
Vertex tem=ver_translate[(int)v.list.get(i)-1]; //定义为该点的下一个点
//当tem点未读,则对该点递归进行深度优先搜索
if(visited_translate[tem.n-1]==false)
translate_DFS(tem);
i++;
}
}
public static void main(String args[])
{
csh(); //初始化
print(ver); //打印邻接链表
csh1();
System.out.println("");
for(int i=0;i<len_v;i++)
{
if(!s.contains(ver[i]))
DFS2(ver[i]); //DFS并求拓扑
}
System.out.println("DAG图拓扑排序:"+sta.size());
for(int i=0;i<sta.size();i++)
{
System.out.print(sta.get(sta.size()-1-i)+"-->");
}
System.out.println("");
System.out.println("-------图反转-------");
translate(ver); //100个点500个边可能只有一个连通分量,可以使用(10,50)来测试
print(ver_translate);
System.out.println("");
csh1();
System.out.println("反转图递归DFS求强连通分支:");
int t=0;
while(t<sta.size())
{
int temp=sta.get(sta.size()-1-t);
if(!translate_stack.contains(temp))
{
System.out.print("(");
translate_DFS(ver_translate[temp-1]);//非递归深度优先搜索
System.out.print(")");
System.out.println("");
}
t++;
}
}
}