[java] Simple implementation of judging whether there is a cycle in the graph

question

Java implements the basic data structure of the graph and judges whether there is a cycle in the graph.

accomplish

GraphRelationPairThe relationship between the two points in the figure is as follows to the entity class


import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
/**
 * 图的关系对
 */
public class GraphRelationPair {
    
    
    // 当前节点
    private String point;
    
    // 下一个节点
    private String nextPoint;

    static GraphRelationPair of(String point, String nextPoint) {
    
    
        return new GraphRelationPair(point, nextPoint);
    }
}

If you need to add additional information, you can inherit this class.

GraphUtilGraph tool class, which realizes the following functions:
1. Construct the adjacency list of the directed graph, and judge whether there is a cycle in the directed graph;
2. Build the parts table of the undirected graph, and judge whether there is a cycle in the undirected graph.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;

import org.apache.commons.lang.StringUtils;

public class GraphUtil {
    
    
    public static void main(String[] args) {
    
    
        Set<GraphRelationPair> graph = buildData();
        // boolean haveLoop = IsHaveLoop.isHaveLoop(graph);
        // System.out.println(haveLoop);

        boolean dGHaveLoop = GraphUtil.dGHaveLoop(graph);
        System.out.println("【图】是否有环结果:" + dGHaveLoop);
    }

    private static Set<GraphRelationPair> buildData() {
    
    
        Set<GraphRelationPair> refs = new HashSet<>();
        refs.add(GraphRelationPair.of("0", "1"));
        refs.add(GraphRelationPair.of("0", "2"));
        refs.add(GraphRelationPair.of("0", "4"));
        refs.add(GraphRelationPair.of("1", "3"));
        refs.add(GraphRelationPair.of("2", "4"));
        refs.add(GraphRelationPair.of("4", "5"));
        refs.add(GraphRelationPair.of("5", "2"));
        refs.add(GraphRelationPair.of("5", "3"));
        return refs;
    }

    /**
     * 构建 有向图 邻接表
     * 
     * @return
     */
    public static Map<String, List<String>> builDGAdj(Set<GraphRelationPair> graph) {
    
    

        Map<String, List<String>> adj = new HashMap<String, List<String>>();
        if (Objects.isNull(graph) || graph.isEmpty()) {
    
    
            return adj;
        }
        for (GraphRelationPair edg : graph) {
    
    
            String node1 = edg.getPoint();
            String node2 = edg.getNextPoint();
            if (adj.get(node1) == null) {
    
    
                adj.put(node1, new ArrayList<>());
            }
            if (adj.get(node2) == null) {
    
    
                adj.put(node2, new ArrayList<>());
            }
            adj.get(node1).add(node2);
        }
        return adj;
    }

    /**
     * 有向图中判断是否有环
     * 
     * @param graph 图的连接边
     * @param n     图的节点个数
     * @return 是否存在环
     */
    public static boolean dGHaveLoop(Set<GraphRelationPair> graph) {
    
    
        // 习惯上转换成临接表的形式
        Map<String, List<String>> adj = builDGAdj(graph);
        // 定义一个节点状态数组 判断是否访问过
        Map<String, Boolean> visited = new HashMap<>();
        Stack<String> visitedStack = null;
        Set<String> keySet = adj.keySet();
        for (String key : keySet) {
    
    
            visited.put(key, false);
        }
        // 引用传递 函数内部修改值后退出函数可见
        for (String key : keySet) {
    
    
            visitedStack = new Stack<>();
            // 如果没有进行访问 则进行深度优先搜索回溯
            if (!visited.get(key)) {
    
    
                boolean dfs = dgDfsCycle(adj, key, "", visited, visitedStack);
                if (dfs) {
    
    
                    return true;
                } else {
    
    
                    visited.put(key, false);
                    visitedStack.pop();
                }
            }
        }
        return false;
    }

    /**
     * @param adj     图的临接表
     * @param current 当前节点
     * @param parent  父节点
     * @param visited 判断是否访问
     */
    private static boolean dgDfsCycle(Map<String, List<String>> adj, String current, String parent,
            Map<String, Boolean> visited, Stack<String> visitedStack) {
    
    
        // 首先 访问当前节点 并进行标记
        visited.put(current, true);
        visitedStack.push(current);

        // 获取到当前节点能够到达的所有节点
        List<String> list = adj.get(current);
        for (String can : list) {
    
    
            // 如果节点没有被访问过
            if (!visited.get(can)) {
    
    
                // 当前节点就是父节点,循环的节点就是子节点
                return dgDfsCycle(adj, can, current, visited, visitedStack);
            }
            // 在节点被访问过的情况下 说明有环
            else {
    
    
                return visitedStack.contains(can);
            }
        }
        return false;
    }

    /**
     * 构建无向图 邻接表
     * 
     * @return
     */
    public static Map<String, List<String>> buildUGAdj(Set<GraphRelationPair> graph) {
    
    

        Map<String, List<String>> adj = new HashMap<String, List<String>>();
        if (Objects.isNull(graph) || graph.isEmpty()) {
    
    
            return adj;
        }
        for (GraphRelationPair edg : graph) {
    
    
            String node1 = edg.getPoint();
            String node2 = edg.getNextPoint();
            if (adj.get(node1) == null) {
    
    
                adj.put(node1, new ArrayList<>());
            }
            if (adj.get(node2) == null) {
    
    
                adj.put(node2, new ArrayList<>());
            }
            adj.get(node1).add(node2);
            adj.get(node2).add(node1);
        }
        return adj;
    }

    /**
     * 无向图中判断是否有环
     * 
     * @param graph 图的连接边
     * @param n     图的节点个数
     * @return 是否存在环
     */
    public static boolean isHaveLoop(Set<GraphRelationPair> graph) {
    
    
        // 习惯上转换成临接表的形式
        Map<String, List<String>> adj = buildUGAdj(graph);
        // 定义一个节点状态数组 判断是否访问过
        Map<String, Boolean> visited = new HashMap<>();
        Set<String> keySet = adj.keySet();
        for (String key : keySet) {
    
    
            visited.put(key, false);
        }
        // 引用传递 函数内部修改值后退出函数可见
        int[] a = {
    
     0 };
        for (String key : keySet) {
    
    
            // 如果没有进行访问 则进行深度优先搜索回溯
            if (visited.get(key) == false) {
    
    
                dfsCycle(adj, key, "", visited, a);
                // 只要有一次i循环时存在环路那就直接提前返回,说明存在环
                if (a[0] == 1) {
    
    
                    return true;
                }
            }
        }
        return a[0] == 1;
    }

    /**
     * @param adj     图的临接表
     * @param current 当前节点
     * @param parent  父节点
     * @param visited 判断是否访问
     * @param flag    是否存在环
     */
    private static void dfsCycle(Map<String, List<String>> adj, String current, String parent,
            Map<String, Boolean> visited, int[] flag) {
    
    
        // 首先 访问当前节点 并进行标记
        visited.put(current, true);
        // 获取到当前节点能够到达的所有节点
        List<String> list = adj.get(current);
        for (String can : list) {
    
    
            // 如果节点没有被访问过
            if (visited.get(can) == false) {
    
    
                // 当前节点就是父节点,循环的节点就是子节点
                dfsCycle(adj, can, current, visited, flag);
            }
            // 在节点被访问过的情况下 如果该节点不等于父节点 ,说明有环
            else if (!StringUtils.equals(can, parent)) {
    
    
                flag[0] = 1;
            }
        }
    }
}

attached

adjacency list

An adjacency list is a data structure that represents a graph where each vertex is associated with a list of its neighbors. Here is a table representation:

Vertex 1: 2, 3, 4
Vertex 2: 1, 3
Vertex 3: 1, 2, 4
Vertex 4: 1, 3

In this example, the graph consists of four vertices. Vertex 1 is adjacent to vertices 2, 3, 4, vertex 2 is adjacent to vertices 1, 3, and so on. With an adjacency list, we can easily look up the list of adjacent vertices for each vertex.

DFS algorithm

DFS (Depth-First Search) is a depth-first search, which is a traversal algorithm for graphs and trees. It continues to explore other branches by exploring the branches of the graph as deeply as possible until it can no longer continue, and then backtracking to the last incompletely explored node.

The following is a sample code for traversing a graph using the DFS algorithm:

import java.util.*;

// 图的顶点类
class GraphNode {
    
    
    int val;
    List<GraphNode> neighbors;

    public GraphNode(int val) {
    
    
        this.val = val;
        this.neighbors = new ArrayList<>();
    }
}

public class DFS {
    
    
    private Set<GraphNode> visited // 用于记录访问过的顶点

    public DFS() {
    
    
        this.visited = new HashSet<>();
    }

    // 图的深度优先搜索
    public void depthFirstSearch(GraphNode node) {
    
    
        if (visited.contains(node)) {
    
    
            return;
        }

        visited.add(node);
        System.out.print(node.val + " ");

        for (GraphNode neighbor : node.neighbors) {
    
    
            depthFirstSearch(neighbor);
        }
    }

    public static void main(String[] args) {
    
    
        // 创建图的顶点
        GraphNode node1 = new GraphNode(1);
        GraphNode node2 = new GraphNode(2);
        GraphNode node3 = new GraphNode(3);
        GraphNode node4 = new GraphNode(4);

        // 设置顶点之间的关系
        node1.neighbors.add(node2);
        node1.neighbors.add(node3);
        node2.neighbors.add(node3);
        node3.neighbors.add(node4);

        DFS dfs = new DFS();
        dfs.depthFirstSearch(node1);
    }
}

In this example, we create a directed graph and use GraphNodeclasses to represent the vertices of the graph. DFSThe class implements the depth-first search algorithm, where depthFirstSearchthe method receives a vertex of a graph as input, and recursively depth traverses the vertices directly connected to this vertex, and prints their values.

In mainthe method, we create a directed graph and set the relationship between the vertices. Then create DFSthe object and call depthFirstSearchthe method to do a depth-first search starting from the starting vertex of the graph.

directed graph

A Directed Graph, also known as a directed graph or directed network, is a type of graph in which the edges in the graph have directions. In a directed graph, edges point from one vertex to another and have a definite direction, vertices represent entities or nodes in the graph, and directed edges represent directed relationships or connections between nodes. Each edge points from one vertex (called the start vertex or source vertex) to another vertex (called the end vertex or target vertex), so there is a directivity from the start vertex to the end vertex.

Unlike directed graphs, edges in undirected graphs have no direction and can travel in both directions. Directed graphs can be used to represent many real-world relationships, such as links between web pages, traffic routes, dependencies, etc.

Directed graphs can be represented in a variety of ways, including adjacency matrices and adjacency lists. For algorithms and traversal of directed graphs, the directionality of edges needs to be considered. For example, Depth-First Search (DFS) and Breadth-First Search (BFS) algorithms can be applied to directed graphs, but the direction of edges should be paid attention to when traversing to prevent infinite loops.

Undirected graph

An undirected graph, also known as an undirected graph, is a type of graph in which the edges in the graph have no direction. In an undirected graph, an edge connects two vertices, expresses a relationship or connection between the two vertices, but does not specify unidirectionality from one vertex to another.

An undirected graph can be viewed as a collection of unordered edge sets and vertex sets. Each edge connects two vertices, and there is bidirectional traffic between the two vertices. For example, if there is an edge connecting vertex A to vertex B, it is possible to go from A to B and also from B to A.

Undirected graphs have practical applications in many situations, such as human relationships in social networks, communication links in computer networks, and road networks in maps, etc.

Undirected graphs can be represented in a variety of ways, including adjacency matrices and adjacency lists. For the algorithm and traversal of undirected graphs, commonly used graph traversal algorithms such as depth-first search (DFS) and breadth-first search (BFS) can be directly applied. When traversing an undirected graph, there is no need to consider the directionality of edges because edges have no specified direction.

Guess you like

Origin blog.csdn.net/m0_47406832/article/details/131479661