Графики на Java

Графики на Java

1. обзор

В этом руководствеwe’ll understand the basic concepts of a graph as a data structure.

Мы также рассмотрим его реализацию на Java вместе с различными операциями, возможными на графике. Мы также обсудим библиотеки Java, предлагающие реализации графов.

2. Структура данных графика

График - этоdata structure for storing connected data как сеть людей в социальной сети.

Граф состоит из вершин и ребер. A vertex represents the entity (например, люди) иan edge represents the relationship between entities (например, дружба человека).

Давайте определим простой график, чтобы лучше понять это:

image image

Здесь мы определили простой граф с пятью вершинами и шестью ребрами. Круги - это вершины, представляющие людей, а линии, соединяющие две вершины, - это ребра, представляющие друзей на онлайн-портале.

Есть несколько вариаций этого простого графика в зависимости от свойств ребер. Давайте кратко рассмотрим их в следующих разделах.

Однако мы сосредоточимся только на простом графике, представленном здесь для примеров Java в этом руководстве.

2.1. Направленный граф

У графа, который мы до сих пор определили, есть ребра без направления. Если этиedges feature a direction in them, результирующий граф известен как ориентированный граф.

Примером этого может служить представление того, кто отправил запрос на добавление в друзья в онлайн-портал:

image image

Здесь мы видим, что ребра имеют фиксированное направление. Края также могут быть двунаправленными.

2.2. Взвешенный график

Опять же, наш простой граф имеет ребра, которые являются несмещенными или не взвешенными. Если вместо этихedges carry relative weight, этот график известен как взвешенный график.

Примером практического применения этого может быть представление о том, сколько лет дружбы на онлайн-портале:

image image

Здесь мы можем видеть, что у ребер есть веса, связанные с ними. Это обеспечивает относительное значение для этих краев.

3. Graph Representationsс

Граф может быть представлен в различных формах, таких как матрица смежности и список смежности. У каждого есть свои плюсы и минусы в разных настройках.

Мы представим эти графические представления в этом разделе.

3.1. Матрица смежности

Матрица смежности -a square matrix with dimensions equivalent to the number of vertices в графе.

Элементы матрицы обычно имеют значения 0 или 1. Значение «1» указывает на смежность между вершинами в строке и столбце, а значение «0» в противном случае.

Давайте посмотрим, как выглядит матрица смежности для нашего простого графа из предыдущего раздела:

image image

Это представление также довольноeasier to implement and efficient to query. Однако этоless efficient with respect to space occupied.

3.2. Список смежности

Список смежности - это не что иное, какan array of lists. Размер массива эквивалентен количеству вершин в графе.

Список с определенным индексом массива представляет смежные вершины вершины, представленной этим индексом массива.

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

image image

Это представлениеcomparatively difficult to create and less efficient to query. Однако он предлагаетbetter space efficiency.

Мы будем использовать список смежности для представления графика в этом руководстве.

4. Графики на Java

В Java нет стандартной реализации структуры данных графа.

Тем не менее, мы можем реализовать граф, используя Java Collections.

Начнем с определения вершины:

class Vertex {
    String label;
    Vertex(String label) {
        this.label = label;
    }

    // equals and hashCode
}

Приведенное выше определение вершины просто имеет метку, но она может представлять любую возможную сущность, напримерPerson илиCity..

Также обратите внимание, чтоwe have to override the equals() and hashCode() methods as these are necessary to work with Java Collections.

Как мы уже обсуждали ранее, граф - это не что иное, как набор вершин и ребер, которые могут быть представлены либо как матрица смежности, либо как список смежности.

Давайте посмотрим, как мы можем определить это, используя список смежности здесь:

class Graph {
    private Map> adjVertices;

    // standard constructor, getters, setters
}

Как мы видим здесь, классGraph используетMap из коллекций Java для определения списка смежности.

Со структурой данных графа возможно несколько операций, напримерcreating, updating or searching through the graph.

Мы рассмотрим некоторые из наиболее распространенных операций и посмотрим, как их реализовать на Java.

5. Операции мутации графов

Для начала мы определим несколько методов для изменения структуры данных графа.

Давайте определим методы для добавления и удаления вершин:

void addVertex(String label) {
    adjVertices.putIfAbsent(new Vertex(label), new ArrayList<>());
}

void removeVertex(String label) {
    Vertex v = new Vertex(label);
    adjVertices.values().stream().forEach(e -> e.remove(v));
    adjVertices.remove(new Vertex(label));
}

Эти методы просто добавляют и удаляют элементы изvertices Set.

Теперь давайте также определим метод добавления ребра:

void addEdge(String label1, String label2) {
    Vertex v1 = new Vertex(label1);
    Vertex v2 = new Vertex(label2);
    adjVertices.get(v1).add(v2);
    adjVertices.get(v2).add(v1);
}

Этот метод создает новыйEdge и обновляет соседние вершиныMap.

Аналогичным образом мы определим методremoveEdge():

void removeEdge(String label1, String label2) {
    Vertex v1 = new Vertex(label1);
    Vertex v2 = new Vertex(label2);
    List eV1 = adjVertices.get(v1);
    List eV2 = adjVertices.get(v2);
    if (eV1 != null)
        eV1.remove(v2);
    if (eV2 != null)
        eV2.remove(v1);
}

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

Graph createGraph() {
    Graph graph = new Graph();
    graph.addVertex("Bob");
    graph.addVertex("Alice");
    graph.addVertex("Mark");
    graph.addVertex("Rob");
    graph.addVertex("Maria");
    graph.addEdge("Bob", "Alice");
    graph.addEdge("Bob", "Rob");
    graph.addEdge("Alice", "Mark");
    graph.addEdge("Rob", "Mark");
    graph.addEdge("Alice", "Maria");
    graph.addEdge("Rob", "Maria");
    return graph;
}

Наконец, мы определим метод получения смежных вершин конкретной вершины:

List getAdjVertices(String label) {
    return adjVertices.get(new Vertex(label));
}

6. Обход графа

Теперь, когда у нас есть структура данных графа и определенные функции для его создания и обновления, мы можем определить некоторые дополнительные функции для обхода графа. Нам нужно пройти по графику, чтобы выполнить какое-либо значимое действие, например поиск по графику.

Естьtwo possible ways to traverse a graph, depth-first traversal and breadth-first traversal.

6.1. Глубина-первый обход

Обход в глубину начинается с произвольной корневой вершины иexplores vertices as deeper as possible along each branch before exploring vertices at the same level.

Давайте определим метод для выполнения обхода в глубину:

Set depthFirstTraversal(Graph graph, String root) {
    Set visited = new LinkedHashSet();
    Stack stack = new Stack();
    stack.push(root);
    while (!stack.isEmpty()) {
        String vertex = stack.pop();
        if (!visited.contains(vertex)) {
            visited.add(vertex);
            for (Vertex v : graph.getAdjVertices(vertex)) {
                stack.push(v.label);
            }
        }
    }
    return visited;
}

Здесьwe’re using a Stack to store the vertices that need to be traversed.

Давайте запустим это на графике, который мы создали в предыдущем подразделе:

assertEquals("[Bob, Rob, Maria, Alice, Mark]", depthFirstTraversal(graph, "Bob").toString());

Обратите внимание, что здесь мы используем вершину Bob в качестве корня для обхода, но это может быть любая другая вершина.

6.2. Обход в ширину

Для сравнения, обход в ширину начинается с произвольной корневой вершины иexplores all neighboring vertices at the same level before going deeper на графе.

Теперь давайте определим метод для выполнения обхода в ширину:

Set breadthFirstTraversal(Graph graph, String root) {
    Set visited = new LinkedHashSet();
    Queue queue = new LinkedList();
    queue.add(root);
    visited.add(root);
    while (!queue.isEmpty()) {
        String vertex = queue.poll();
        for (Vertex v : graph.getAdjVertices(vertex)) {
            if (!visited.contains(v.label)) {
                visited.add(v.label);
                queue.add(v.label);
            }
        }
    }
    return visited;
}

Обратите внимание, что обход в ширинуmakes use of Queue to store the vertices which need to be traversed.

Давайте снова запустим этот обход на том же графике:

assertEquals("[Bob, Alice, Rob, Mark, Maria]", breadthFirstTraversal(graph, "Bob").toString());

Опять же, корневая вершина, которая здесь называется «Боб», также может быть любой другой вершиной.

7. Библиотеки Java для графиков

Необязательно всегда реализовывать граф с нуля в Java. Существует несколько доступных библиотек с открытым исходным кодом, которые предлагают реализацию графов.

В следующих нескольких подразделах мы рассмотрим некоторые из этих библиотек.

7.1. JGraphT

JGraphT - одна из самых популярных библиотек в Java для структуры данных графа. Это позволяет создавать простой граф, ориентированный граф, взвешенный граф, среди других.

Кроме того, он предлагает множество возможных алгоритмов для структуры данных графа. Одно из наших предыдущих руководств посвященоJGraphT in much more detail.

7.2. Google Guava

Google Guava - это набор библиотек Java, которые предлагают ряд функций, включая структуру данных графа и ее алгоритмы.

Он поддерживает создание простыхGraph,ValueGraph иNetwork. Их можно определить какMutable илиImmutable.

7.3. Apache Commons

Apache Commons - это проект Apache, который предлагает повторно используемые компоненты Java. Это включает в себя Commons Graph, который предлагает инструментарий для создания и управления структурой данных графа. Это также предоставляет общие графовые алгоритмы для работы со структурой данных.

7.4. Sourceforge JUNG

Java Universal Network/Graph (JUNG) - это среда Java, которая предоставляет расширяемый язык для моделирования, анализа и визуализации любых данных, которые могут быть представлены в виде графика.

JUNG поддерживает ряд алгоритмов, которые включают в себя такие процедуры, как кластеризация, декомпозиция и оптимизация.

 

Эти библиотеки предоставляют ряд реализаций, основанных на структуре данных графа. Также естьmore powerful frameworks based on graphs, такие какApache Giraph, которые в настоящее время используются в Facebook для анализа графов, сформированных их пользователями, иApache TinkerPop, обычно используемые поверх баз данных графов.

8. Заключение

В этой статье мы обсудили граф как структуру данных вместе с его представлениями. Мы определили очень простой граф в Java, используя Java Collections, а также определили общие обходы для графа.

Мы также кратко рассказали о различных библиотеках, доступных в Java за пределами платформы Java, которая обеспечивает реализацию графов.

Как всегда, доступен код для примеровover on GitHub.