查找指定组件中的所有连接(简单、断开、无向图)
Find all connections in a specified component (simple, disconnected, undirected graph)
我有以下简单的、断开的、无向图:
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'}
}
我创建了一个关于我的想法的GitHub Gist and a Replit snippet。
在我的解决方案中,我使用了 adjacency matrix,这首先是相当昂贵的...
但是非常有帮助,而且维护起来也很便宜。
便宜...尤其是没有 updateComponentMapping
这些是我 Graph<T>
class:
底部的静态助手
//...
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
中填充 ID 以标记连接的组件:
//...
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 && componentMapping[j] == j) {
componentMapping[j] = componentId;
}
}
}
}
//...
之后打印就很简单了:
//...
public int getNodeId(T node) {
int index = nodeList.indexOf(node);
if (0 <= index && 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();
}
我是大 O 表示法的新手,但我认为:
- 第一次构建邻接矩阵:O(n2)
- 向邻接矩阵添加新连接:O(1)
- 更新组件映射:O(n2)
- 打印连接也是:O(n2)
我有以下简单的、断开的、无向图:
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'}
}
我创建了一个关于我的想法的GitHub Gist and a Replit snippet。
在我的解决方案中,我使用了 adjacency matrix,这首先是相当昂贵的...
但是非常有帮助,而且维护起来也很便宜。
便宜...尤其是没有 updateComponentMapping
这些是我 Graph<T>
class:
//...
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
中填充 ID 以标记连接的组件:
//...
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 && componentMapping[j] == j) {
componentMapping[j] = componentId;
}
}
}
}
//...
之后打印就很简单了:
//...
public int getNodeId(T node) {
int index = nodeList.indexOf(node);
if (0 <= index && 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();
}
我是大 O 表示法的新手,但我认为:
- 第一次构建邻接矩阵:O(n2)
- 向邻接矩阵添加新连接:O(1)
- 更新组件映射:O(n2)
- 打印连接也是:O(n2)