Topological sorting realizes circular dependency judgment | JD Cloud technical team

This article records how to implement circular dependency judgment through topological sorting

Preface

Generally speaking, when circular dependencies are mentioned, the first thing that comes to mind is the circular dependency detection of beans provided by the Spring framework. For related documents, please refer to:

https://blog.csdn.net/cristianoxm/article/details/113246104

This article's solution breaks away from Spring Bean management and completes the judgment of object circular dependencies through algorithm implementation. The knowledge points involved include: adjacency matrix graph, topological sorting, and circular dependencies. This article will focus on the technical implementation, and the specific algorithm principles will not be repeated.

Concept explanation

1. What is an adjacency matrix?

The adjacency matrix to be summarized here is the adjacency matrix about the graph; the storage method of the adjacency matrix of the graph is to use two arrays to represent the graph; a one-dimensional array stores the vertex information in the graph, and a two-dimensional array (called adjacency Matrix) stores information about edges or arcs in the graph;

Graphs are divided into directed graphs and undirected graphs, and their corresponding adjacency matrices are also different. The adjacency matrix of an undirected graph is a symmetric matrix, which is a symmetric two-digit array, a[i][j] = a[j ][i];
The adjacency matrix can clearly know whether any two vertices of the graph have an edge; it is convenient to calculate the degree of any vertex (including the out-degree and in-degree of the directed graph); it can intuitively see the adjacent points of any vertex ;

In this case, the directed adjacency matrix graph is one of the necessary conditions for topological sorting, followed by the in-degree of each vertex of the directed adjacency matrix graph.

2. What is the storage structure of adjacency matrix?

vexs[MAXVEX] This is the vertex table;

arc[MAXVEX][MAXVEX] This is the adjacency matrix graph, which is also a two-dimensional array that stores information about each edge. The index of the array is the two vertices of the edge, and the data of the array is the weight of the edge;

numVertexes, numEdges are the number of vertices and edges of the graph respectively.

3. What is the in-degree of the vertices of the directed adjacency matrix graph?

In a directed graph, arrows have directions, pointing from one vertex to another vertex. In this way, the number of arrows pointed to each vertex is its in-degree. The number of arrows pointing out from this vertex is its out-degree

The row number of the adjacency matrix represents the starting node of the arrow, and the column number is the pointing node of the arrow. Therefore, a 1 in the same row in the matrix means that there is an edge from the i-th node to the j-th node, and in the same column If it is 1, it means that the j-th node is pointed to by the i-th node. Therefore, if you want the in-degree or out-degree of a vertex, you only need to determine the number of 1s in the same column or the number of 1s in the same row.

4. What is topological sorting?

Elements of topological sorting:
1. Directed acyclic graph;
2. Each point in the sequence can only appear once;
3. For any pair of u and v, u always comes before v (the two letters here represent respectively The two endpoints of a line segment, u represents the starting point and v represents the end point);

According to the elements of topological sorting, whether there is a cycle in object dependency can be judged through its directed acyclicity. If a graph composed of objects can complete topological sorting, then there is no cycle in the object graph, that is, there is no circular dependency between objects.

In addition to being implemented through directed adjacency matrix graphs, topological sorting can also be implemented through depth-first search (DFS). In this case, only the former will be explained.

5. What are circular dependencies?

A simple explanation is as follows. If there are two objects, if A needs B to create, and B needs A to create, these two objects are dependent on each other, forming the simplest circular dependency relationship.

Programming examples

1. Object entity

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class RelationVo implements Serializable {

    /**
     * 唯一标识
     */
    private String uniqueKey;

    /**
     * 关联唯一标识集合
     */
    private List combinedUniqueKeys;

}

2. Convert the object collection into a directed adjacency matrix graph

    /**
     * 将List集合转换为邻接矩阵图的二维数组形式
     *
     * @param sourceList
     * @return
     */
    public static int[][] listToAdjacencyMatrixDiagram(List sourceList) {

        List distinctRelationVoList = new ArrayList(sourceList);
        List keyCollect = distinctRelationVoList.stream().map(RelationVo::getUniqueKey).collect(Collectors.toList());

        for (RelationVo vo : sourceList) {
            vo.getCombinedUniqueKeys().forEach(child -> {
                if (!keyCollect.contains(child)) {
                    // 若叶子节点不在集合中,补充List集合中单独叶子节点,目的是完成提供邻接矩阵图计算的入参
                    keyCollect.add(child);
                    RelationVo build = RelationVo.builder().uniqueKey(child).build();
                    distinctRelationVoList.add(build);
                }
            });
        }

        // 顶点数:对象中出现的全部元素总数
        int vertexNum = keyCollect.size();
        /*
         * 初始化邻接矩阵图的边的二维数组,1表示有边 0表示无边 权重均为1
         * 其中数组下标为边的两个顶点,数组值为对象边的权值(权值=是否有边*权重)
         */
        int[][] edges = new int[vertexNum][vertexNum];

        // 计算邻接矩阵图
        for (int i = 0; i < vertexNum; i++) {
            RelationVo colVo = distinctRelationVoList.get(i);
            List colUniqueKeys = colVo.getCombinedUniqueKeys();
            for (int j = 0; j < vertexNum; j++) {
                RelationVo rowVo = distinctRelationVoList.get(j);
                String rowVertex = rowVo.getUniqueKey();
                if (CollUtil.isNotEmpty(colUniqueKeys)) {
                    if (colUniqueKeys.contains(rowVertex)) {
                        edges[i][j] = 1;
                    } else {
                        edges[i][j] = 0;
                    }
                }
            }
        }
        return edges;
    }
     

3. Calculate the in-degree of the vertices of the adjacency matrix graph

     /**
     * 返回给出图每个顶点的入度值
     *
     * @param adjMatrix 给出图的邻接矩阵值
     * @return
     */
    public static int[] getSource(int[][] adjMatrix) {
        int len = adjMatrix[0].length;
        int[] source = new int[len];
        for (int i = 0; i < len; i++) {
            // 若邻接矩阵中第i列含有m个1,则在该列的节点就包含m个入度,即source[i] = m
            int count = 0;
            for (int j = 0; j < len; j++) {
                if (adjMatrix[j][i] == 1) {
                    count++;
                }
            }
            source[i] = count;
        }
        return source;
    }
    

4. Topological sorting of adjacency matrix graph

    /**
     * 拓扑排序,返回给出图的拓扑排序序列
     * 拓扑排序基本思想:
     * 方法1:基于减治法:寻找图中入度为0的顶点作为即将遍历的顶点,遍历完后,将此顶点从图中删除
     * 若结果集长度等于图的顶点数,说明无环;若小于图的顶点数,说明存在环
     *
     * @param adjMatrix 给出图的邻接矩阵值
     * @param source    给出图的每个顶点的入度值
     * @return
     */
    public static List topologySort(int[][] adjMatrix, int[] source) {
        // 给出图的顶点个数
        int len = source.length;
        // 定义最终返回路径字符数组
        List result = new ArrayList(len);

        // 获取入度为0的顶点下标
        int vertexFound = findInDegreeZero(source);

        while (vertexFound != -1) {
            result.add(vertexFound);
            // 代表第i个顶点已被遍历
            source[vertexFound] = -1;
            for (int j = 0; j < adjMatrix[0].length; j++) {
                if (adjMatrix[vertexFound][j] == 1) {
                    // 第j个顶点的入度减1
                    source[j] -= 1;
                }
            }
            vertexFound = findInDegreeZero(source);

        }
        //输出拓扑排序的结果
        return result;

    }

    /**
     * 找到入度为0的点,如果存在入度为0的点,则返回这个点;如果不存在,则返回-1
     *
     * @param source 给出图的每个顶点的入度值
     * @return
     */
    public static int findInDegreeZero(int[] source) {
        for (int i = 0; i < source.length; i++) {
            if (source[i] == 0) {
                return i;
            }
        }
        return -1;
    }
     

5. Check whether there are circular dependencies in the collection

    /**
     * 检查集合是否存在循环依赖
     *
     * @param itemList
     */
    public static void checkCircularDependency(List itemList) throws Exception {
        if (CollUtil.isEmpty(itemList)) {
            return;
        }
        // 计算邻接矩阵图的二维数组
        int[][] edges = listToAdjacencyMatrixDiagram(itemList);
        // 计算邻接矩阵图每个顶点的入度值
        int[] source = getSource(edges);
        // 拓扑排序得到拓扑序列
        List topologySort = topologySort(edges, source);
        if (source.length == topologySort.size()) {
            // 无循环依赖
            return;
        } else {
            // 序列集合与顶点集合大小不一致,存在循环依赖
            throw new Exception("当前险种关系信息存在循环依赖,请检查");
        }
    }

Single test case

1. Test material-no circular dependencies

示例JSON Array结构(可完成拓扑排序):
[{
    "uniqueKey":"A",
    "combinedUniqueKeys":[
        "C",
        "D",
        "E"
    ]
},
{
    "uniqueKey":"B",
    "combinedUniqueKeys":[
        "D",
        "E"
    ]
},
{
    "uniqueKey":"D",
    "combinedUniqueKeys":[
        "C"
    ]
}
]


2. Test materials - there are circular dependencies

示例JSON Array结构(不可完成拓扑排序):
[{
    "uniqueKey":"A",
    "combinedUniqueKeys":[
        "C",
        "D",
        "E"
    ]
},
{
    "uniqueKey":"B",
    "combinedUniqueKeys":[
        "D",
        "E"
    ]
},
{
    "uniqueKey":"D",
    "combinedUniqueKeys":[
        "C"
    ]
},
{
    "uniqueKey":"C",
    "combinedUniqueKeys":[
        "B"
    ]
}
]

3. Single test example

@Slf4j
public class CircularDependencyTest {
    /**
     * 针对集合信息判断该集合是否存在循环依赖
     */
    @Test
    void testCircularDependencyList() throws Exception {
        String paramInfo = "[{\"uniqueKey\":\"A\",\"combinedUniqueKeys\":[\"C\",\"D\",\"E\"]},{\"uniqueKey\":\"B\",\"combinedUniqueKeys\":[\"D\",\"E\"]},{\"uniqueKey\":\"D\",\"combinedUniqueKeys\":[\"C\"]}]";
        // 序列化
        List list = JSONArray.parseArray(paramInfo, RelationVo.class);
        TopologicalSortingUtil.checkCircularDependency(list);
    }
}

 

Author: JD Insurance Hou Yadong

Source: JD Cloud Developer Community Please indicate the source when reprinting

IntelliJ IDEA 2023.3 & JetBrains Family Bucket annual major version update new concept "defensive programming": make yourself a stable job GitHub.com runs more than 1,200 MySQL hosts, how to seamlessly upgrade to 8.0? Stephen Chow's Web3 team will launch an independent App next month. Will Firefox be eliminated? Visual Studio Code 1.85 released, floating window Yu Chengdong: Huawei will launch disruptive products next year and rewrite the history of the industry. The US CISA recommends abandoning C/C++ to eliminate memory security vulnerabilities. TIOBE December: C# is expected to become the programming language of the year. A paper written by Lei Jun 30 years ago : "Principle and Design of Computer Virus Determination Expert System"
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10320441