[java] Implementação simples de julgar se existe um ciclo no gráfico

pergunta

Java implementa a estrutura básica de dados do gráfico e julga se existe um ciclo no gráfico.

concluir

GraphRelationPairA relação entre os dois pontos na figura é a seguinte para a classe de entidade


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);
    }
}

Se precisar adicionar informações adicionais, você pode herdar esta classe.

GraphUtilClasse de ferramenta de gráfico, que realiza as seguintes funções:
1. Construir a lista de adjacências do gráfico direcionado e julgar se existe um ciclo no gráfico direcionado;
2. Construir a tabela de partes do gráfico não direcionado e julgar se existe um ciclo no gráfico não direcionado.

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;
            }
        }
    }
}

apegado

lista de adjacências

Uma lista de adjacências é uma estrutura de dados que representa um grafo onde cada vértice está associado a uma lista de seus vizinhos. Aqui está uma representação de tabela:

Vértice 1: 2, 3, 4
Vértice 2: 1, 3
Vértice 3: 1, 2, 4
Vértice 4: 1, 3

Neste exemplo, o gráfico consiste em quatro vértices. O vértice 1 é adjacente aos vértices 2, 3, 4, o vértice 2 é adjacente aos vértices 1, 3 e assim por diante. Com uma lista de adjacências, podemos facilmente consultar a lista de vértices adjacentes para cada vértice.

Algoritmo DFS

DFS (Depth-First Search) é uma pesquisa em profundidade, que é um algoritmo de passagem para gráficos e árvores. Ele continua a explorar outras ramificações, explorando as ramificações do gráfico o mais profundamente possível até que não possa mais continuar e, em seguida, retrocedendo até o último nó explorado de forma incompleta.

A seguir está um exemplo de código para percorrer um gráfico usando o algoritmo DFS:

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);
    }
}

Neste exemplo, criamos um gráfico direcionado e usamos GraphNodeclasses para representar os vértices do gráfico. DFSA classe implementa o algoritmo de busca em profundidade, onde depthFirstSearcho método recebe um vértice de um grafo como entrada, e recursivamente percorre em profundidade os vértices diretamente conectados a este vértice, e imprime seus valores.

No mainmétodo, criamos um gráfico direcionado e definimos a relação entre os vértices. Em seguida, crie DFSo objeto e chame depthFirstSearcho método para fazer uma pesquisa em profundidade começando no vértice inicial do gráfico.

gráfico direcionado

Um gráfico direcionado, também conhecido como gráfico direcionado ou rede direcionada, é um tipo de gráfico no qual as arestas do gráfico têm direções. Em um gráfico direcionado, as arestas apontam de um vértice para outro e têm uma direção definida, os vértices representam entidades ou nós no gráfico e as arestas direcionadas representam relacionamentos direcionados ou conexões entre nós. Cada aresta aponta de um vértice (chamado de vértice inicial ou vértice de origem) para outro vértice (chamado de vértice final ou vértice de destino), portanto, há uma diretividade do vértice inicial para o vértice final.

Ao contrário dos gráficos direcionados, as arestas em gráficos não direcionados não têm direção e podem viajar em ambas as direções. Os gráficos direcionados podem ser usados ​​para representar muitos relacionamentos do mundo real, como links entre páginas da web, rotas de tráfego, dependências, etc.

Os gráficos direcionados podem ser representados de várias maneiras, incluindo matrizes de adjacência e listas de adjacência. Para algoritmos e travessia de gráficos direcionados, a direcionalidade das arestas precisa ser considerada. Por exemplo, os algoritmos Depth-First Search (DFS) e Breadth-First Search (BFS) podem ser aplicados a gráficos direcionados, mas deve-se prestar atenção à direção das arestas ao percorrer para evitar loops infinitos.

Gráfico não direcionado

Um gráfico não direcionado, também conhecido como gráfico não direcionado, é um tipo de gráfico no qual as arestas do gráfico não têm direção. Em um gráfico não direcionado, uma aresta conecta dois vértices, expressa um relacionamento ou conexão entre os dois vértices, mas não especifica a unidirecionalidade de um vértice para outro.

Um gráfico não direcionado pode ser visto como uma coleção de conjuntos de arestas e conjuntos de vértices não ordenados. Cada aresta conecta dois vértices e há tráfego bidirecional entre os dois vértices. Por exemplo, se houver uma aresta conectando o vértice A ao vértice B, é possível ir de A a B e também de B a A.

Os gráficos não direcionados têm aplicações práticas em muitas situações, como relações humanas em redes sociais, links de comunicação em redes de computadores e redes rodoviárias em mapas, etc.

Os gráficos não direcionados podem ser representados de várias maneiras, incluindo matrizes de adjacência e listas de adjacência. Para o algoritmo e travessia de gráficos não direcionados, algoritmos de travessia de grafos comumente usados, como pesquisa em profundidade (DFS) e pesquisa em largura (BFS), podem ser aplicados diretamente. Ao percorrer um gráfico não direcionado, não há necessidade de considerar a direcionalidade das arestas porque as arestas não têm direção especificada.

Acho que você gosta

Origin blog.csdn.net/m0_47406832/article/details/131479661
Recomendado
Clasificación