Java实现有向图求强连通分支--Kosaraju算法

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++;
            }

        }

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值