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(たとえば、人の友情)。
これをよりよく理解するために、簡単なグラフを定義しましょう。
ここでは、5つの頂点と6つのエッジを持つ単純なグラフを定義しました。 円は人を表す頂点であり、2つの頂点を結ぶ線はオンラインポータル上の友人を表すエッジです。
エッジのプロパティに応じて、この単純なグラフにはいくつかのバリエーションがあります。 次のセクションで簡単に説明しましょう。
ただし、このチュートリアルのJavaの例については、ここに示す単純なグラフのみに焦点を当てます。
2.1. 有向グラフ
これまでに定義したグラフには、方向のないエッジがあります。 これらのedges feature a direction in themの場合、結果のグラフは有向グラフと呼ばれます。
この例として、オンラインポータルのフレンドシップでフレンドリクエストを送信した人を表すことができます。
ここでは、エッジの方向が固定されていることがわかります。 エッジも双方向にすることができます。
3. Graph Representations
グラフは、隣接行列や隣接リストなどのさまざまな形式で表すことができます。 それぞれに長所と短所があります。
このセクションでは、これらのグラフ表現を紹介します。
3.1. 隣接行列
隣接行列はグラフのa square matrix with dimensions equivalent to the number of verticesです。
マトリックスの要素は通常、値「0」または「1」を持ちます。 「1」の値は、行と列の頂点間の隣接を示し、それ以外の場合は「0」の値を示します。
前のセクションの単純なグラフの隣接行列がどのように見えるかを見てみましょう。
この表現もかなりeasier to implement and efficient to queryです。 ただし、それはless efficient with respect to space occupiedです。
4. Javaのグラフ
Javaには、グラフデータ構造のデフォルトの実装がありません。
ただし、Javaコレクションを使用してグラフを実装できます。
頂点を定義することから始めましょう:
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はJavaコレクションのMapを使用して隣接リストを定義しています。
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で最も人気のあるライブラリの1つです。 これにより、単純なグラフ、有向グラフ、加重グラフなどを作成できます。
さらに、グラフのデータ構造で可能な多くのアルゴリズムを提供します。 以前のチュートリアルの1つは、JGraphT in much more detailをカバーしています。
7.2. Google Guava
Google Guavaは、グラフデータ構造とそのアルゴリズムを含むさまざまな関数を提供するJavaライブラリのセットです。
単純なGraph、ValueGraph、およびNetworkの作成をサポートします。 これらは、MutableまたはImmutableとして定義できます。
7.3. Apache Commons
Apache Commonsは、再利用可能なJavaコンポーネントを提供するApacheプロジェクトです。 これには、グラフデータ構造を作成および管理するためのツールキットを提供するCommons Graphが含まれます。 これは、データ構造を操作する一般的なグラフアルゴリズムも提供します。
7.4. Sourceforge JUNG
Java Universal Network/Graph (JUNG)は、グラフとして表現できるデータのモデリング、分析、および視覚化のための拡張可能な言語を提供するJavaフレームワークです。
JUNGは、クラスタリング、分解、最適化などのルーチンを含む多くのアルゴリズムをサポートしています。
これらのライブラリは、グラフのデータ構造に基づいて多くの実装を提供します。 また、Facebookでユーザーが作成したグラフを分析するために現在使用されているApache Giraphなどのmore powerful frameworks based on graphsや、グラフデータベース上で一般的に使用されているApache TinkerPopもあります。
8. 結論
この記事では、グラフをその表現とともにデータ構造として説明しました。 Javaコレクションを使用してJavaで非常に単純なグラフを定義し、グラフの共通トラバーサルも定義しました。
また、グラフ実装を提供するJavaプラットフォーム以外のJavaで使用可能なさまざまなライブラリについても簡単に説明しました。
いつものように、例のコードはover on GitHubで利用できます。