Найдите все соединения в указанном компоненте (простой, отключенный, неориентированный график)

#java #algorithm #data-structures #graph #graph-algorithm

Вопрос:

У меня есть следующий простой, несвязный, неориентированный график:

 G1 = (V1, E1)  
V1 = {'a', 'b', 'c', 'd', 'e', 'f', 'k', 'l', 'x', 'y', 'z'}  
E1 = {
        {'a', 'b'}, {'a', 'c'},  
        {'d', 'e'}, {'d', 'f'},  
        {'k', 'l'},  
        {'x', 'y'}, {'x', 'z'}
     }
 

Я храню его таким образом:

 private final Map<String, List<String>> graph = Map.of(
    "a", List.of("b", "c"),
    "d", List.of("e", "f"),
    "k", List.of("l"),
    "x", List.of("y", "z"));
 

Мне нужно реализовать следующий метод:

 public void apply(String connectFrom, List<String> connectTos)
 

Если мы вызовем его, он должен применить новое соединение(соединения) и вывести каждое соединение из подключенного компонента, в котором from (в этом примере: «a») находится:

 apply("a", List.of("d", "k"));
 

Результат должен быть таким:

 ['a - b','a - c','a - d','a - e','a - f','a - k','a - l','b - a','b - c','b - d','b - e','b - f','b - k','b - l','c - a','c - b','c - d','c - e','c - f','c - k','c - l','d - a','d - b','d - c','d - e','d - f','d - k','d - l','e - a','e - b','e - c','e - d','e - f','e - k','e - l','f - a','f - b','f - c','f - d','f - e','f - k','f - l','k - a','k - b','k - c','k - d','k - e','k - f','k - l','l - a','l - b','l - c','l - d','l - e','l - f','l - k']
 

В этом примере выходные данные содержат все соединения графика, исключая:

 ['x - y', 'x - z', 'y - x', 'y - z', 'z - x', 'z - y']
 

Потому {'x', 'y'}, {'x', 'z'} что образует отдельный компонент:

 G1 = (V1, E1)  
V1 = {'a', 'b', 'c', 'd', 'e', 'f', 'k', 'l', 'x', 'y', 'z'}  
E1 = {
        {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'a', 'k'},  
        {'d', 'e'}, {'d', 'f'},  
        {'k', 'l'},  
        {'x', 'y'}, {'x', 'z'}
     }
 

Комментарии:

1. Что вы собираетесь делать apply ? найдите соединение между узлом и списком других узлов ? Если это так, используйте поиск Сначала по ширине , Если вы хотите apply найти все соединения, используйте Поиск сначала по глубине

2. могу ли я получить указанные выше выходные данные с помощью BFS или DFS?

3. Пожалуйста, прочитайте, для чего и как используются BFS и DFS. Для получения дополнительной помощи опубликуйте графическое представление дерева и объясните результат. (Подсказка добавьте @c0der в комментарий, чтобы адресовать его мне)

Ответ №1:

Я создал суть GitHub и повторил фрагмент моей идеи.

В своем решении я использовал матрицу смежности, которая сначала довольно дорогая…
Но очень полезная и дешевая в обслуживании.
Дешевый… Особенно без updateComponentMapping

Это мои статические помощники из низших классов моего Graph<T> класса:

 //...
    public static <T extends Comparable<T>> Set<T> getNodeSet(Map<T, List<T>> graph) {
        Set<T> nodeSet = new TreeSet<>(graph.keySet());
        nodeSet.addAll(
            graph.values().stream()
                .flatMap(List::stream)
                .collect(Collectors.toSet()));

        return nodeSet;
    }

    public static <T extends Comparable<T>> int[][] getAdjacencyMatrix(Map<T, List<T>> graph) {
        return getAdjacencyMatrix(graph, getNodeSet(graph));
    }

    public static <T extends Comparable<T>> int[][] getAdjacencyMatrix(Map<T, List<T>> graph, Set<T> nodes) {
        List<T> nodeList = new ArrayList<>(nodes);
        int[][] matrix = new int[nodeList.size()][nodeList.size()];

        graph.forEach(
            (from, toList) ->
                toList.forEach(
                    to -> setAdjacency(matrix, nodeList.indexOf(from), nodeList.indexOf(to), 1)));

        return matrix;
    }

    private static void setAdjacency(int[][] matrix, int from, int to, int value) {
        matrix[from][to] = value;
        matrix[to][from] = value;
    }
}
 

На основе статических вспомогательных методов я создаю Graph<T> объект:

 public static class Graph<T extends Comparable<T>> {
    private final List<T> nodeList;
    private final int[][] adjacencyMatrix;
    private final int[] componentMapping;

    public Graph(Map<T, List<T>> graph) {
        var nodeSet = getNodeSet(graph);
        nodeList = new ArrayList<>(nodeSet);
        adjacencyMatrix = getAdjacencyMatrix(graph, nodeSet);
        componentMapping = new int[nodeList.size()];
        updateComponentMapping();
    }

// ...
 

Где я componentMapping заполняю идентификаторы для маркировки подключенных компонентов:

 //...

    private void updateComponentMapping() {
        IntStream.range(0, componentMapping.length).forEach(
            i -> componentMapping[i] = i
        );

  
        int componentId;
        for (int i = 0; i < componentMapping.length; i  ) {
            componentId = componentMapping[i];

            for (int j = i; j < componentMapping.length; j  ) {

                if (adjacencyMatrix[i][j] == 1 amp;amp; componentMapping[j] == j) {
                    componentMapping[j] = componentId;
                }
            }
        }
    }

//...
 

После этого печать становится довольно простой:

 //...

    public int getNodeId(T node) {
        int index = nodeList.indexOf(node);

        if (0 <= index amp;amp; index < nodeList.size()) {
            return index;
        } else {
            throw new IllegalArgumentException("Node index is out of range.");
        }
    }

    public int getComponentId(T node) {
        return componentMapping[getNodeId(node)];
    }

    public void printAllConnections() {
        printComponentConnections(-1);
    }

    public void printComponentConnections(int componentId) {
        StringJoiner result = new StringJoiner("','", "['", "']");

        nodeList.forEach(
            from -> {
                if (getComponentId(from) == componentId || componentId < 0) {
                    nodeList.forEach(
                        to ->  {
                            if (isConnected(from, to)) {
                                result.add(String.format("%s - %s", from, to));
                            }
                        }
                    );
                }});
        System.out.println(result);
    }

    public boolean isConnected(T from, T to) {
        return isConnected(getNodeId(from), getNodeId(to));
    }

    public boolean isConnected(int indexFrom, int indexTo) {
        return indexFrom == indexTo
            ? adjacencyMatrix[indexFrom][indexTo] == 1
            : componentMapping[indexFrom] == componentMapping[indexTo];
    }

//...
 

Но нам также нужны новые связи, поэтому есть addConnection и его помощники:

 //...
    public void addConnections(T from, List<T> to) {
        int fromIndex = getNodeId(from);
        to.forEach(node -> connect(fromIndex, getNodeId(node)));
        updateComponentMapping();
    }

    private void connect(int from, int to) {
        set(from, to, 1);
    }

    private void disconnect(int from, int to) {
        set(from, to, 0);
    }

    private void set(int from, int to, int value) {
        setAdjacency(adjacencyMatrix, from, to, value);
    }
//...
 

И наконец…

 private static final Map<String, List<String>> graphMap =
    Map.of(
        "a", List.of("b", "c"),
        "d", List.of("e", "f"),
        "k", List.of("l"),
        "x", List.of("y", "z"));

public static void main(String[] args) {
    var graph = new Graph<>(graphMap);
    graph.addConnections("a", List.of("d", "k"));

    int componentId = graph.getComponentId("a");
    graph.printComponentConnections(componentId);
    //graph.printAllConnections();
}
 

Я новичок в нотации Big O, но я думаю, что:

  • Построение матрицы смежности с первого раза: O(n 2)
  • Добавьте новые соединения в матрицу смежности: O(1)
  • Обновление сопоставления компонентов: O(n 2)
  • Подключения для печати также: O(n 2)

Комментарии:

1. Большое спасибо, работая как ожидалось, мне, возможно, придется немного изменить в соответствии с требованием, но это должно быть нормально, один вопрос, почему вы выбрали матрицу смежности вместо списка смежности?

2. Пожалуйста. 🙂 Мне было просто любопытно, и это задание было интересным. 😉 Да, я выбираю немного другие методы, потому что двойная функция apply (создание соединений и печать) отвлекала меня. О списке смежности… Я новичок в мире алгоритмов, и матрица была для меня удобной и понятной. 🙂 Это решение основано на моем опыте, теперь пришло время найти лучшее. 🙂

3. может быть, следующим вызовом, который вы можете принять, чтобы реализовать то же самое, используя список смежности 😛

4. Или без изобретения колеса. 🙂 Приятные объяснения и куча библиотек: «Графики на Java» на Beldung