Java实现有向图去环得到DAG

本文介绍了一种基于深度优先搜索(DFS)的算法,用于检测并去除有向图中的环。通过给图中的每个节点标记三种状态,有效地识别并断开形成环的边。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法思想:

假如单纯使用DFS判断某节点邻接链表中的点是否已被标注,得不出正确结果。比如:A->B,A->C->B,我们用DFS来处理这个图,则会判断为它有环,(A->C->B中的B已被标记过),但其实该图没有环。
因此可以对DFS稍加变化来解决这个问题。解决的方法如下:

对于图中的一个节点,根据其C[V]的值,有三种状态:

  • C[V] = 0,表示此节点没有被访问过
  • C[V] = -1,表示此节点被访问过至少1次,其后代节点正在被访问中
  • C[V] = 1,表示其后代节点都被访问过。

按照这样的假设,当按照DFS进行搜索时,碰到一个节点时有三种可能:

  • 如果C[V]=0,这是一个新的节点,不做处理
  • 如果C[V]=-1,说明是在访问该节点的后代的过程中访问到该节点本身,则图中有环。
  • 如果C[V]=1,没有环。
实现代码:

算法代码为 void Qu_huan(Vertex v)
前面的是构造图的方法

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 DAG {

    static int len_v=10;   //点的个数
    static int len_e=15;   //边的个数
    private static boolean visited2[]=new boolean[len_v];  //DFS中被访问的点
    static Vertex ver[]=new Vertex[len_v];  //存放点
    static int num[]=new int[len_v];  //链表中此时被访问的点
    static int c[]=new int[len_v];  //该节点是否被访问过
    static Stack<Vertex> s=new Stack<Vertex>();  //非递归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()
    {
        //构建len_v个点
        for(int i=0;i<len_v;i++)
        {
            ver[i]=create(i+1);
            visited2[i]=false;
        }

        //构建Len_e条边
        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);
        }

        //将len_e条边加到对应的起始点的链表中
        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-len_v
        int i=(int) (Math.random()*len_v+1);
        return i;
    }

    //构造len_v个点的邻接链表
    public static Vertex create(int n)
    {
        return new Vertex(n);
    }

        //删除边
        public static void delete(Vertex u,Vertex v)
        {
            for(int i=0;i<u.list.size();i++)
                if(u.list.get(i).equals(v.n))
                   u.list.remove(i);
        }

        //非递归DFS去除有向图中的环
        public static void Qu_huan(Vertex v)
        {
            visited2[v.n-1]=true;  //将参数中对应点标为已读
            s.add(v);  //将初始点加入栈中

            while(!s.empty())
            {
                while(num[v.n-1]<v.list.size())
                {
                    //System.out.println("打印:");
                    c[v.n-1]=-1;
                    Vertex tem=ver[(int)v.list.get(num[v.n-1])-1]; //定义为该点的下一个点

                    //System.out.println(v.list.size());
                    if(visited2[tem.n-1]==false)
                    {
                        s.add(tem);  //将该点邻接表中第一个(未读)邻接点放入栈中
                        num[v.n-1]++;  //下一次搜索时,避免搜索已遍历过的点
                        visited2[tem.n-1]=true; //把刚才放入的点标记为已读
                    }

                    else
                    {
                      if(c[tem.n-1]==-1)
                      {
                        delete(v,tem);  //删除成环的边
                      }
                      else
                          num[v.n-1]++;
                    }

                    v=s.peek();  // v=刚才放入的那个点(即继续向下一层进行while寻找)
                    //System.out.println(num[v.n-1]+"**"+v.list.size());

                }
                c[v.n-1]=1;

                s.pop();  //这一条深度搜寻链已到底,将最底下的那个点从栈中删除

                if(!s.empty())
                   v=s.peek();  //回溯到上一个点
            }

        }

        public static void main(String args[])
        {
            csh();  //初始化
            print(ver);  //打印邻接链表

            csh1();
            System.out.println("");

            for(int i=0;i<len_v;i++)
            {
                num[i]=0;
                c[i]=0;
            }

            for(int i=0;i<len_v;i++)
            {
                if(!s.contains(ver[i]))
                    Qu_huan(ver[i]);  //去环
            }

            System.out.println("去环:");
            print(ver);


        }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值