ssslinppp

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

有向无环图

任务的执行有依赖关系,如下图所示:

可以使用DAG(有向无环图)来维护这种依赖关系;

定义task

@Data
@NoArgsConstructor
public class NodeTask {
    private String id;
    private Set<String> dependences = Sets.newConcurrentHashSet();  //依赖的taskID

    public NodeTask(String id) {
        this.id = id;
    }

    public NodeTask addDependence(String nodeTaskId) {
        this.dependences.add(nodeTaskId);
        return this;
    }
}

Graph

package com.ssslinppp.graph.directd;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.MutableGraph;

import java.util.List;
import java.util.Map;
import java.util.Queue;

public class TaskGraph {
    private MutableGraph<NodeTask> taskGraph = GraphBuilder.directed().allowsSelfLoops(false).build();

    /**
     * 转换节点任务为Graph
     *
     * @param nodeTasks
     */
    public void parseNodeTasksToGraph(Map<String, NodeTask> nodeTasks) {
        for (NodeTask nodeTask : nodeTasks.values()) {
            if (!taskGraph.nodes().contains(nodeTask)) {
                taskGraph.addNode(nodeTask);
            }

            for (String dependence : nodeTask.getDependences()) {
                taskGraph.putEdge(nodeTask, nodeTasks.get(dependence));
            }
        }
    }

    /**
     * 判断是否为DAG(Directed Acyclic Graph)有向无环图
     * <p>
     * 算法思路:
     * <ul>
     * <li>1. 根据"拓扑排序"算法判断:拓扑排序之后,若还剩有点,则表示有环</li>
     * <li>2. 拓扑排序算法:找到图中所有入度为0的点,放入序列,删除这些点和以这些点为出度的边,再找所有入度为0的点,依次循环</li>
     * </ul>
     *
     * @return
     */
    public boolean isDAGraph() {
        Map<String, Integer> nodeInDegreeMap = Maps.newHashMap();
        Queue<NodeTask> queue = Queues.newArrayDeque();
        List<String> topologicalSortList = Lists.newArrayList(); //拓扑排序列表维护

        // 获取所有入度为0的节点
        for (NodeTask nodeTask : taskGraph.nodes()) {
            int indegree = taskGraph.inDegree(nodeTask);
            nodeInDegreeMap.put(nodeTask.getId(), indegree);
            if (indegree == 0) {
                queue.add(nodeTask);
                topologicalSortList.add(nodeTask.getId());
            }
        }

        while (!queue.isEmpty()) {
            NodeTask preNode = queue.poll(); //获取并删除

            for (NodeTask successorNode : taskGraph.successors(preNode)) {
                int indegree = nodeInDegreeMap.get(successorNode.getId());
                if (--indegree == 0) {//-1:等效删除父节点以及相应的边
                    queue.offer(successorNode); //insert
                    topologicalSortList.add(successorNode.getId());
                }
                nodeInDegreeMap.put(successorNode.getId(), indegree);
            }
        }

        System.out.println("拓扑排序(topologicalSortList):" + topologicalSortList);

        if (topologicalSortList.size() != taskGraph.nodes().size()) {
            return false;
        }

        return true;
    }

    /**
     * 打印Graph中task的依赖关系
     */
    public void print() {
        System.out.println("=============NodeTask count: " + taskGraph.nodes().size());
        for (NodeTask nodeTask : taskGraph.nodes()) {
            System.out.println("-------- NodeTask: " + nodeTask.getId() + "--------");
            System.out.print("Dependent on: ");
            taskGraph.successors(nodeTask).forEach((v) -> System.out.print(v.getId() + ", "));
            System.out.println();
            System.out.print("all predecessors: ");
            taskGraph.predecessors(nodeTask).forEach((v) -> System.out.print(v.getId() + ", "));
            System.out.println();
            System.out.println();
        }
    }
}

Test

package com.ssslinppp.graph.directd;

import com.google.common.collect.Maps;
import org.junit.Test;

import java.util.Map;

public class TaskGraphTest {
    /**
     * 测试依赖关系
     */
    @Test
    public void testDependence() {
        Map<String, NodeTask> nodeTaskMap = Maps.newConcurrentMap();
        NodeTask nodeTaskA = new NodeTask("nodeTaskA");
        NodeTask nodeTaskB = new NodeTask("nodeTaskB");
        NodeTask nodeTaskC = new NodeTask("nodeTaskC").addDependence("nodeTaskA");
        NodeTask nodeTaskD = new NodeTask("nodeTaskD").addDependence("nodeTaskB");
        NodeTask nodeTaskE = new NodeTask("nodeTaskE").addDependence("nodeTaskC").addDependence("nodeTaskD");
        NodeTask nodeTaskF = new NodeTask("nodeTaskF").addDependence("nodeTaskE");
        NodeTask nodeTaskG = new NodeTask("nodeTaskG").addDependence("nodeTaskE");
        nodeTaskMap.put(nodeTaskA.getId(), nodeTaskA);
        nodeTaskMap.put(nodeTaskB.getId(), nodeTaskB);
        nodeTaskMap.put(nodeTaskC.getId(), nodeTaskC);
        nodeTaskMap.put(nodeTaskD.getId(), nodeTaskD);
        nodeTaskMap.put(nodeTaskE.getId(), nodeTaskE);
        nodeTaskMap.put(nodeTaskF.getId(), nodeTaskF);
        nodeTaskMap.put(nodeTaskG.getId(), nodeTaskG);

        TaskGraph taskGraph = new TaskGraph();
        taskGraph.parseNodeTasksToGraph(nodeTaskMap);
        System.out.println("======== DAG(有向无环图)判断 ===========");
        if (taskGraph.isDAGraph()) {
            System.out.println("is DAG");
        } else {
            System.out.println("Not DAG");
        }

        System.out.println("=============== print ===========");
        taskGraph.print();
    }


    /**
     * 判断是否为有向无环图
     */
    @Test
    public void testDAG() {
        // E依赖D, D依赖B, B依赖E ==>(B,E,G)为一个环
        Map<String, NodeTask> nodeTaskMap = Maps.newConcurrentMap();
        NodeTask nodeTaskA = new NodeTask("nodeTaskA");
        NodeTask nodeTaskB = new NodeTask("nodeTaskB");
//        nodeTaskB.addDependence("nodeTaskE");  // 在这里控制是否有环,进行测试
        NodeTask nodeTaskC = new NodeTask("nodeTaskC").addDependence("nodeTaskA");
        NodeTask nodeTaskD = new NodeTask("nodeTaskD").addDependence("nodeTaskB");
        NodeTask nodeTaskE = new NodeTask("nodeTaskE").addDependence("nodeTaskC").addDependence("nodeTaskD");
        NodeTask nodeTaskF = new NodeTask("nodeTaskF").addDependence("nodeTaskE");
        NodeTask nodeTaskG = new NodeTask("nodeTaskG").addDependence("nodeTaskE");
        nodeTaskMap.put(nodeTaskA.getId(), nodeTaskA);
        nodeTaskMap.put(nodeTaskB.getId(), nodeTaskB);
        nodeTaskMap.put(nodeTaskC.getId(), nodeTaskC);
        nodeTaskMap.put(nodeTaskD.getId(), nodeTaskD);
        nodeTaskMap.put(nodeTaskE.getId(), nodeTaskE);
        nodeTaskMap.put(nodeTaskF.getId(), nodeTaskF);
        nodeTaskMap.put(nodeTaskG.getId(), nodeTaskG);

        TaskGraph taskGraph = new TaskGraph();
        taskGraph.parseNodeTasksToGraph(nodeTaskMap);
        System.out.println("======== DAG(有向无环图)判断 ===========");
        if (taskGraph.isDAGraph()) {
            System.out.println("It is DAG");
        } else {
            System.out.println("Not DAG");
        }
    }
}

pom

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

源码

源码Github

posted on 2018-03-29 17:59  ssslinppp  阅读(2151)  评论(0编辑  收藏  举报