Java实现有向图去环得到DAG

算法思想:

假如单纯使用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);


        }

}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用JavaScript和SVG实现Web前端WorkFlow工作流DAG有向无的步骤: 1. 首先,需要定义一个包含节点和边的数据结构,可以使用邻接表或邻接矩阵来表示。 2. 接下来,需要使用SVG来绘制节点和边。可以使用SVG的circle元素来绘制节点,使用line元素来绘制边。 3. 在绘制节点和边之前,需要计算每个节点的位置。可以使用拓扑排序算法来计算节点的位置,确保没有。 4. 绘制节点和边时,可以使用鼠标事件来实现节点的拖拽和边的连线。 5. 最后,可以添加一些交互式功能,例如缩放和平移。 以下是一个简单的JavaScript和SVG实现Web前端WorkFlow工作流DAG有向无的示例代码: ```javascript // 定义节点和边的数据结构 var nodes = [ { id: 1, name: 'Node 1', x: 100, y: 100 }, { id: 2, name: 'Node 2', x: 200, y: 200 }, { id: 3, name: 'Node 3', x: 300, y: 100 } ]; var edges = [ { source: 1, target: 2 }, { source: 2, target: 3 } ]; // 计算节点的位置 var layout = dagre.layout() .nodeSep(50) .rankSep(50) .run({ nodes: nodes, edges: edges }); // 绘制节点和边 var svg = d3.select('svg'); var g = svg.append('g'); var node = g.selectAll('.node') .data(nodes) .enter() .append('g') .attr('class', 'node') .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; }); node.append('circle') .attr('r', 20) .style('fill', 'white') .style('stroke', 'black') .style('stroke-width', '2px'); node.append('text') .attr('dy', '.35em') .text(function(d) { return d.name; }); var edge = g.selectAll('.edge') .data(edges) .enter() .append('line') .attr('class', 'edge') .attr('x1', function(d) { return layout.nodes[d.source].x; }) .attr('y1', function(d) { return layout.nodes[d.source].y; }) .attr('x2', function(d) { return layout.nodes[d.target].x; }) .attr('y2', function(d) { return layout.nodes[d.target].y; }) .style('stroke', 'black') .style('stroke-width', '2px'); // 添加交互式功能 var zoom = d3.zoom() .on('zoom', function() { g.attr('transform', d3.event.transform); }); svg.call(zoom); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值