diff --git a/pom.xml b/pom.xml
index f3c88c5c5..61902a166 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
com.github.btrekkie.connectivity
dynamic-connectivity
- 0.1.1
+ 0.1.2
dynamic-connectivity
Data structure for dynamic connectivity in undirected graphs
@@ -43,7 +43,7 @@
com.github.btrekkie
RedBlackNode
- 1.0.0
+ 1.0.1
junit
diff --git a/src/main/java/com/github/btrekkie/connectivity/ConnGraph.java b/src/main/java/com/github/btrekkie/connectivity/ConnGraph.java
index c60b7db2f..2150dd9fb 100644
--- a/src/main/java/com/github/btrekkie/connectivity/ConnGraph.java
+++ b/src/main/java/com/github/btrekkie/connectivity/ConnGraph.java
@@ -75,8 +75,8 @@ import java.util.Map;
* reason they are probabilistic is that they involve hash lookups, using the vertexInfo and VertexInfo.edges hash maps.
* Given that each ConnVertex has a random hash code, it is easy to demonstrate that lookups take O(1) expected time.
* Furthermore, I claim that they take O(log N / log log N) time with high probability. This claim is sufficient to
- * establish that all time bounds that are at least O(log N / log log N) if we exclude the hash lookup can be sustained
- * if we add the qualifier "with high probability."
+ * establish that all time bounds that are at least O(log N / log log N) if we exclude hash lookups can be sustained if
+ * we add the qualifier "with high probability."
*
* This claim is based on information presented in
* https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-851-advanced-data-structures-spring-2012/lecture-videos/session-10-dictionaries/ .
@@ -258,7 +258,7 @@ public class ConnGraph {
/**
* Equivalent implementation is contractual.
*
- * This method is useful for when a node's lists (graphListHead or forestListHead) or a vertex's arbitrary visit
+ * This method is useful for when an EulerTourVertex's lists (graphListHead or forestListHead) or arbitrary visit
* change, as these affect the hasGraphEdge and hasForestEdge augmentations.
*/
private void augmentAncestorFlags(EulerTourNode node) {
@@ -505,6 +505,33 @@ public class ConnGraph {
return new EulerTourEdge(newNode, max);
}
+ /** Removes the specified edge from the Euler tour forest F_i. */
+ private void removeForestEdge(EulerTourEdge edge) {
+ EulerTourNode firstNode;
+ EulerTourNode secondNode;
+ if (edge.visit1.compareTo(edge.visit2) < 0) {
+ firstNode = edge.visit1;
+ secondNode = edge.visit2;
+ } else {
+ firstNode = edge.visit2;
+ secondNode = edge.visit1;
+ }
+
+ if (firstNode.vertex.arbitraryVisit == firstNode) {
+ EulerTourNode successor = secondNode.successor();
+ firstNode.vertex.arbitraryVisit = successor;
+ augmentAncestorFlags(firstNode);
+ augmentAncestorFlags(successor);
+ }
+
+ EulerTourNode root = firstNode.root();
+ EulerTourNode[] firstSplitRoots = root.split(firstNode);
+ EulerTourNode before = firstSplitRoots[0];
+ EulerTourNode[] secondSplitRoots = firstSplitRoots[1].split(secondNode.successor());
+ before.concatenate(secondSplitRoots[1]);
+ firstNode.removeWithoutGettingRoot();
+ }
+
/**
* Adds the specified edge to the edge map for srcInfo (srcInfo.edges). Assumes that the edge is not currently in
* the map.
@@ -794,31 +821,8 @@ public class ConnGraph {
augmentAncestorFlags(edge.vertex2.arbitraryVisit);
if (edge.eulerTourEdge != null) {
- // Remove the edge from all of the Euler tour trees that contain it
for (EulerTourEdge levelEdge = edge.eulerTourEdge; levelEdge != null; levelEdge = levelEdge.higherEdge) {
- EulerTourNode firstNode;
- EulerTourNode secondNode;
- if (levelEdge.visit1.compareTo(levelEdge.visit2) < 0) {
- firstNode = levelEdge.visit1;
- secondNode = levelEdge.visit2;
- } else {
- firstNode = levelEdge.visit2;
- secondNode = levelEdge.visit1;
- }
-
- if (firstNode.vertex.arbitraryVisit == firstNode) {
- EulerTourNode successor = secondNode.successor();
- firstNode.vertex.arbitraryVisit = successor;
- augmentAncestorFlags(firstNode);
- augmentAncestorFlags(successor);
- }
-
- EulerTourNode root = firstNode.root();
- EulerTourNode[] firstSplitRoots = root.split(firstNode);
- EulerTourNode before = firstSplitRoots[0];
- EulerTourNode[] secondSplitRoots = firstSplitRoots[1].split(secondNode.successor());
- before.concatenate(secondSplitRoots[1]);
- firstNode.removeWithoutGettingRoot();
+ removeForestEdge(levelEdge);
}
edge.eulerTourEdge = null;
@@ -1044,18 +1048,10 @@ public class ConnGraph {
}
/**
- * Attempts to optimize the internal representation of the graph so that future updates will take less time. This
- * method does not affect how long queries such as "connected" will take. You may find it beneficial to call
- * optimize() when there is some downtime. Note that this method generally increases the amount of space the
- * ConnGraph uses, but not beyond the bound of O(V log V + E).
+ * Pushes all forest edges as far down as possible, so that any further pushes would violate the constraint on the
+ * size of connected components. The current implementation of this method takes O(V log^2 V) time.
*/
- public void optimize() {
- // The current implementation of optimize() takes O(V log^2 V + E log V log log V) time
-
- rebuild();
-
- // Greedily push each forest edge as far down as possible - to the lowest level where the constraint on the
- // size of connected components isn't violated
+ private void optimizeForestEdges() {
for (VertexInfo info : vertexInfo.values()) {
int level = maxLogVertexCountSinceRebuild;
EulerTourVertex vertex;
@@ -1121,8 +1117,13 @@ public class ConnGraph {
level++;
}
}
+ }
- // Push each non-forest edge down to the lowest level where the endpoints are in the same connected component
+ /**
+ * Pushes each non-forest edge down to the lowest level where the endpoints are in the same connected component. The
+ * current implementation of this method takes O(V log V + E log V log log V) time.
+ */
+ private void optimizeGraphEdges() {
for (VertexInfo info : vertexInfo.values()) {
EulerTourVertex vertex;
for (vertex = info.vertex; vertex.lowerVertex != null; vertex = vertex.lowerVertex);
@@ -1185,4 +1186,17 @@ public class ConnGraph {
}
}
}
+
+ /**
+ * Attempts to optimize the internal representation of the graph so that future updates will take less time. This
+ * method does not affect how long queries such as "connected" will take. You may find it beneficial to call
+ * optimize() when there is some downtime. Note that this method generally increases the amount of space the
+ * ConnGraph uses, but not beyond the bound of O(V log V + E).
+ */
+ public void optimize() {
+ // The current implementation of optimize() takes O(V log^2 V + E log V log log V) time
+ rebuild();
+ optimizeForestEdges();
+ optimizeGraphEdges();
+ }
}
diff --git a/target/dynamic-connectivity-0.1.1-jar-with-dependencies.jar b/target/dynamic-connectivity-0.1.1-jar-with-dependencies.jar
deleted file mode 100644
index 9a757a038..000000000
Binary files a/target/dynamic-connectivity-0.1.1-jar-with-dependencies.jar and /dev/null differ
diff --git a/target/dynamic-connectivity-0.1.1.jar b/target/dynamic-connectivity-0.1.1.jar
deleted file mode 100644
index fed9bc68f..000000000
Binary files a/target/dynamic-connectivity-0.1.1.jar and /dev/null differ
diff --git a/target/dynamic-connectivity-0.1.2-jar-with-dependencies.jar b/target/dynamic-connectivity-0.1.2-jar-with-dependencies.jar
new file mode 100644
index 000000000..aeb0bbd55
Binary files /dev/null and b/target/dynamic-connectivity-0.1.2-jar-with-dependencies.jar differ
diff --git a/target/dynamic-connectivity-0.1.2.jar b/target/dynamic-connectivity-0.1.2.jar
new file mode 100644
index 000000000..c82ff9e74
Binary files /dev/null and b/target/dynamic-connectivity-0.1.2.jar differ