DAG有向无环图

参考

  • https://ctolib.com/yueyunyue-liteflow.html

  • DAG有向无环图:https://blog.csdn.net/yanwumuxi/article/details/67633766

  • 算法精解:DAG有向无环图:http://www.cnblogs.com/Evsward/p/dag.html

  • https://time.geekbang.org/column/article/76207

拓扑排序

定义:将有向图中的顶点以线性方式进行排序。即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面。有向边总是按照排序顺序向前,而不是向后。若用1~N来对顶点进行编号,编号代表顶点在拓扑排序中的顺序。N是有向图G的顶点个数。因此,只要能对顶点按照1~N进行编号,有向边总是按照编号顺序向前(有向边可以是从编号1的节点到3的节点,不能是从编号3的节点到编号1的节点)。

  1. 一个有向有环图=>没有拓扑排序

  2. 一个有向无环图=>有拓扑排序

  3. 一个有向无环图一定存在一个节点(sink vertex),这个节点只有指向它的边,没有指向别人的边。

假设有向无环图不存在这个节点,那么从一个节点出发,一定可以找到下一个节点(有指向别人的边),假设一共有N个节点,我从一个节点出发走N次,那我至少遍历了N+1个节点,这说明其中某个节点我至少遍历了两次,这个图是一个有向有环图,与条件矛盾,因此一定存在这个节点。

容易得知,在拓扑排序中最后位置的节点一定是一个sink vertex。

算法的思想就是先找到那个sink vertex,将那个sink vertex标记为N,然后从剩下N-1个节点中找出其他的sink vertex,再标记为N-1……一直到标记到第一个节点为止。

更正:若一个节点,在未标记的节点集合中只有指向它的边(在已标记的节点集合中可以有指向别人的边),这个节点就是最大位置的节点。

广度优先遍历

https://yq.aliyun.com/articles/14429

/**
     * 判断是否有环
     * https://time.geekbang.org/column/article/76207
     * @param graph
     * @return
     */
    public boolean hasCycle(Map<NodeVO, LinkedList<NodeVO>> graph) {
        List<NodeVO> list = topoSortByKahn(graph);
        if(graph.size() > list.size()){
            return true;
        }
        return false;
    }

    /**
     * 拓扑排序 Kahn 算法
     * @param graph
     */
    public List<NodeVO> topoSortByKahn(Map<NodeVO, LinkedList<NodeVO>> graph){
        List<NodeVO> result = new ArrayList<>();
        Map<NodeVO, Integer> inDegree = new HashMap<>();
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            inDegree.put(entry.getKey(), 0);
        }
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            System.out.print("key="+entry.getKey().getId()+",value=");
            for(NodeVO nodeVO: entry.getValue()){
                /**
                 * entry.key -> nodeVO
                 */
                inDegree.put(nodeVO, inDegree.get(nodeVO) + 1);
                System.out.print(nodeVO.getId()+",");
            }
            System.out.println();
        }
        LinkedList<NodeVO> queue = new LinkedList<>();
        for (Map.Entry<NodeVO, Integer> entry: inDegree.entrySet()) {
            if (entry.getValue() == 0) {
                queue.add(entry.getKey());
            }
        }
        System.out.println(queue.get(0).getId());
        while (!queue.isEmpty()) {
            NodeVO i = queue.remove();
            System.out.print("->" + i.getId());
            result.add(i);
            for(NodeVO nodeVO: graph.get(i)){
                inDegree.put(nodeVO, inDegree.get(nodeVO) -1);
                if (inDegree.get(nodeVO) == 0) {
                    queue.add(nodeVO);
                }
            }
        }
        return result;
    }


    /**
     * 找出头节点(入度为0)
     * @param graph
     * @return
     */
    public NodeVO getHeadNode(Map<NodeVO, LinkedList<NodeVO>> graph){
        Map<NodeVO, Integer> inDegree = new HashMap<>();
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            inDegree.put(entry.getKey(), 0);
        }
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            System.out.print("key="+entry.getKey().getId()+",value=");
            for(NodeVO nodeVO: entry.getValue()){
                /**
                 * entry.key -> nodeVO
                 */
                inDegree.put(nodeVO, inDegree.get(nodeVO) + 1);
                System.out.print(nodeVO.getId()+",");
            }
            System.out.println();
        }
        for (Map.Entry<NodeVO, Integer> entry: inDegree.entrySet()) {
            if (entry.getValue() == 0) {
                System.out.print(entry.getKey().getId());
                return entry.getKey();
            }
        }
        return null;
    }

    public int getAllHeadNode(Map<NodeVO, LinkedList<NodeVO>> graph){
        int result = 0;
        Map<NodeVO, Integer> inDegree = new HashMap<>();
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            inDegree.put(entry.getKey(), 0);
        }
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            System.out.print("key="+entry.getKey().getId()+",value=");
            for(NodeVO nodeVO: entry.getValue()){
                /**
                 * entry.key -> nodeVO
                 */
                inDegree.put(nodeVO, inDegree.get(nodeVO) + 1);
                System.out.print(nodeVO.getId()+",");
            }
            System.out.println();
        }
        for (Map.Entry<NodeVO, Integer> entry: inDegree.entrySet()) {
            /**
             * 起点
             */
            if (entry.getValue() == 0) {
                System.out.print(entry.getKey().getId());
                result++;
            }
        }
        return result;
    }

    /**
     * 广度优先遍历
     * @param graph
     */
    public void bfs(Map<NodeVO, LinkedList<NodeVO>> graph){
        Queue<NodeVO> queue = new LinkedList<>();
        queue.offer(getHeadNode(graph));

        Map<NodeVO, Boolean> visitedMap = new HashMap<>();
        for(Map.Entry<NodeVO, LinkedList<NodeVO>> entry: graph.entrySet()){
            visitedMap.put(entry.getKey(), false);
        }
        while(!queue.isEmpty()){
            //从队列中删除下一个节点
            NodeVO v = queue.poll();
            //将该节点的所有邻接节点加入队列中
            for(NodeVO w : graph.get(v)){
                //如果没有标记就标记
                if(!visitedMap.get(w)){
                    visitedMap.put(w,true);
                    System.out.print("->" + w.getId());
                    queue.add(w);
                }
            }
        }

    }

    /**
     * 深度优先遍历
     * @param graph
     */
    public void dfs(Map<NodeVO, LinkedList<NodeVO>> graph){
        searchTraversing(graph,getHeadNode(graph),new ArrayList<>());

    }

    /**
     * 深度遍历
     * DFSearch
     * @param node
     * 			当前节点
     * @param visited
     * 			被访问过的节点列表
     */
    public void searchTraversing(Map<NodeVO, LinkedList<NodeVO>> graph, NodeVO node, List<NodeVO> visited) {
        // 判断是否遍历过
        if (visited.contains(node)) {
            return;
        }
        visited.add(node);
        System.out.print("->" + node.getId());
        for (int i = 0; i < graph.get(node).size(); i++) {
            searchTraversing(graph,graph.get(node).get(i), visited);
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值