reformat all code
This commit is contained in:
@@ -7,7 +7,7 @@ package com.github.btrekkie.connectivity;
|
||||
* combine(combine(combine(A1, A2), A3), A4). In order for an augmentation result to be meaningful, the combining
|
||||
* function must be commutative, meaning combine(x, y) is equivalent to combine(y, x), and associative, meaning
|
||||
* combine(x, combine(y, z)) is equivalent to combine(combine(x, y), z).
|
||||
*
|
||||
* <p>
|
||||
* If a ConnGraph represents a game map, then one example of an augmentation would be the amount of gold accessible from
|
||||
* a certain location. Each vertex would be augmented with the amount of gold in that location, and the combining
|
||||
* function would add the two amounts of gold passed in as arguments. Another example would be the strongest monster
|
||||
@@ -16,18 +16,18 @@ package com.github.btrekkie.connectivity;
|
||||
* in as arguments. A third example would be the number of locations accessible from a given vertex. Each vertex would
|
||||
* be augmented with the number 1, and the combining function would add the two numbers of vertices passed in as
|
||||
* arguments.
|
||||
*
|
||||
* <p>
|
||||
* ConnGraph treats two augmentation values X and Y as interchangeable if they are equal, as in
|
||||
* X != null ? X.equals(Y) : Y == null. The same holds for two combined augmentation values, and for one combined
|
||||
* augmentation value and one augmentation value.
|
||||
*
|
||||
* <p>
|
||||
* See the comments for ConnGraph.
|
||||
*/
|
||||
public interface Augmentation {
|
||||
/**
|
||||
* Returns the result of combining the specified values into one. Each argument is either the augmentation
|
||||
* information associated with a vertex, or the result of a previous call to "combine".
|
||||
*
|
||||
* <p>
|
||||
* Note that a value of null is never passed in to indicate the absence of augmentation information. The fact that
|
||||
* ConnGraph.getVertexAugmentation, for example, may return null when there is no associated augmentation might lead
|
||||
* you to believe that a null argument indicates the absence of augmentation information, but again, it does not. A
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.github.btrekkie.connectivity;
|
||||
* Represents an edge in a ConnGraph, at the level of the edge (i.e. at the lowest level i for which G_i contains the
|
||||
* edge). Every graph edge has exactly one corresponding ConnEdge object, regardless of the number of levels it appears
|
||||
* in. See the comments for the implementation of ConnGraph.
|
||||
*
|
||||
* <p>
|
||||
* ConnEdges are stored in the linked lists suggested by EulerTourVertex.graphListHead and
|
||||
* EulerTourVertex.forestListHead. Each ConnEdge is in two linked lists, so care must be taken when traversing the
|
||||
* linked lists. prev1 and next1 are the links for the list starting at vertex1.graphListHead or vertex1.forestListHead,
|
||||
@@ -14,10 +14,14 @@ package com.github.btrekkie.connectivity;
|
||||
* after next1 is next1.next1, but otherwise, it is next1.next2.
|
||||
*/
|
||||
class ConnEdge {
|
||||
/** The edge's first endpoint (at the same level as the edge). */
|
||||
/**
|
||||
* The edge's first endpoint (at the same level as the edge).
|
||||
*/
|
||||
public EulerTourVertex vertex1;
|
||||
|
||||
/** The edge's second endpoint (at the same level as the edge). */
|
||||
/**
|
||||
* The edge's second endpoint (at the same level as the edge).
|
||||
*/
|
||||
public EulerTourVertex vertex2;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package com.github.btrekkie.connectivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Implements an undirected graph with dynamic connectivity. It supports adding and removing edges and determining
|
||||
@@ -12,7 +8,7 @@ import java.util.Map;
|
||||
* amortized time with high probability, while checking whether two vertices are connected takes O(log N) time with high
|
||||
* probability. It uses O(V log V + E) space, where V is the number of vertices and E is the number of edges. Note that
|
||||
* a ConnVertex may appear in multiple ConnGraphs, with a different set of adjacent vertices in each graph.
|
||||
*
|
||||
* <p>
|
||||
* ConnGraph optionally supports arbitrary augmentation. Each vertex may have an associated augmentation, or value.
|
||||
* Given a vertex V, ConnGraph can quickly report the result of combining the augmentations of all of the vertices in
|
||||
* the connected component containing V, using a combining function provided to the constructor. For example, if a
|
||||
@@ -22,12 +18,12 @@ import java.util.Map;
|
||||
* the augmentation takes a constant amount of space. Retrieving the combined augmentation for a connected component
|
||||
* takes O(log N) time with high probability. (Although ConnGraph does not directly support augmenting edges, this can
|
||||
* also be accomplished, by imputing each edge's augmentation to an adjacent vertex.)
|
||||
*
|
||||
* <p>
|
||||
* When a vertex no longer has any adjacent edges, and it has no augmentation information, ConnGraph stops keeping track
|
||||
* of the vertex. This reduces the time and space bounds of the ConnGraph, and it enables the ConnVertex to be garbage
|
||||
* collected. If you know you are finished with a vertex, and that vertex has an augmentation, then you should call
|
||||
* removeVertexAugmentation on the vertex, so that the graph can release it.
|
||||
*
|
||||
* <p>
|
||||
* As a side note, it would be more proper if ConnGraph had a generic type parameter indicating the type of the
|
||||
* augmentation values. However, it is expected that it is more common not to use augmentation, so by not using a type
|
||||
* parameter, we make usage of the ConnGraph class more convenient and less confusing in the common case.
|
||||
@@ -106,7 +102,7 @@ public class ConnGraph {
|
||||
* The maximum number of vertices we can store in a ConnGraph. This is limited by the fact that EulerTourNode.size
|
||||
* is an int. Since the size of an Euler tour tree is one less than twice the number of vertices in the tree, the
|
||||
* number of vertices may be at most (int)((((long)Integer.MAX_VALUE) + 1) / 2).
|
||||
*
|
||||
* <p>
|
||||
* Of course, we could simply change the "size" field to be a long. But more fundamentally, the number of vertices
|
||||
* is limited by the fact that vertexInfo and VertexInfo.edges use HashMaps. Using a HashMap becomes problematic at
|
||||
* around Integer.MAX_VALUE entries. HashMap buckets entries based on 32-bit hash codes, so in principle, it can
|
||||
@@ -115,7 +111,9 @@ public class ConnGraph {
|
||||
*/
|
||||
private static final int MAX_VERTEX_COUNT = 1 << 30;
|
||||
|
||||
/** The augmentation function for the graph, if any. */
|
||||
/**
|
||||
* The augmentation function for the graph, if any.
|
||||
*/
|
||||
private final Augmentation augmentation;
|
||||
|
||||
/**
|
||||
@@ -139,22 +137,28 @@ public class ConnGraph {
|
||||
*/
|
||||
private int maxVertexInfoSize;
|
||||
|
||||
/** Constructs a new ConnGraph with no augmentation. */
|
||||
/**
|
||||
* Constructs a new ConnGraph with no augmentation.
|
||||
*/
|
||||
public ConnGraph() {
|
||||
augmentation = null;
|
||||
}
|
||||
|
||||
/** Constructs an augmented ConnGraph, using the specified function to combine augmentation values. */
|
||||
/**
|
||||
* Constructs an augmented ConnGraph, using the specified function to combine augmentation values.
|
||||
*/
|
||||
public ConnGraph(Augmentation augmentation) {
|
||||
this.augmentation = augmentation;
|
||||
}
|
||||
|
||||
/** Equivalent implementation is contractual. */
|
||||
/**
|
||||
* Equivalent implementation is contractual.
|
||||
*/
|
||||
private void assertIsAugmented() {
|
||||
if (augmentation == null) {
|
||||
throw new RuntimeException(
|
||||
"You may only call augmentation-related methods on ConnGraph if the graph is augmented, i.e. if an " +
|
||||
"Augmentation was passed to the constructor");
|
||||
"You may only call augmentation-related methods on ConnGraph if the graph is augmented, i.e. if an " +
|
||||
"Augmentation was passed to the constructor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,8 +175,8 @@ public class ConnGraph {
|
||||
|
||||
if (vertexInfo.size() == MAX_VERTEX_COUNT) {
|
||||
throw new RuntimeException(
|
||||
"Sorry, ConnGraph has too many vertices to perform this operation. ConnGraph does not support " +
|
||||
"storing more than ~2^30 vertices at a time.");
|
||||
"Sorry, ConnGraph has too many vertices to perform this operation. ConnGraph does not support " +
|
||||
"storing more than ~2^30 vertices at a time.");
|
||||
}
|
||||
|
||||
EulerTourVertex eulerTourVertex = new EulerTourVertex();
|
||||
@@ -215,9 +219,10 @@ public class ConnGraph {
|
||||
* list for an EulerTourVertex that represents the same underlying ConnVertex, but at a higher level. This has the
|
||||
* effect of prepending the list for the lower level to the beginning of the list for the higher level, and
|
||||
* replacing all links to the lower-level vertex in the ConnEdges with links to the higher-level vertex.
|
||||
* @param head The first node in the list for the higher-level vertex.
|
||||
* @param lowerHead The first node in the list for the lower-level vertex.
|
||||
* @param vertex The higher-level vertex.
|
||||
*
|
||||
* @param head The first node in the list for the higher-level vertex.
|
||||
* @param lowerHead The first node in the list for the lower-level vertex.
|
||||
* @param vertex The higher-level vertex.
|
||||
* @param lowerVertex The lower-level vertex.
|
||||
* @return The head of the combined linked list.
|
||||
*/
|
||||
@@ -257,7 +262,7 @@ public class ConnGraph {
|
||||
|
||||
/**
|
||||
* Equivalent implementation is contractual.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
@@ -301,7 +306,7 @@ public class ConnGraph {
|
||||
}
|
||||
|
||||
vertex.graphListHead =
|
||||
collapseEdgeList(vertex.graphListHead, lowerVertex.graphListHead, vertex, lowerVertex);
|
||||
collapseEdgeList(vertex.graphListHead, lowerVertex.graphListHead, vertex, lowerVertex);
|
||||
if (lowerVertex.forestListHead != null) {
|
||||
// Change the eulerTourEdge links
|
||||
ConnEdge lowerEdge = lowerVertex.forestListHead;
|
||||
@@ -320,7 +325,7 @@ public class ConnGraph {
|
||||
}
|
||||
|
||||
vertex.forestListHead =
|
||||
collapseEdgeList(vertex.forestListHead, lowerVertex.forestListHead, vertex, lowerVertex);
|
||||
collapseEdgeList(vertex.forestListHead, lowerVertex.forestListHead, vertex, lowerVertex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,7 +510,9 @@ public class ConnGraph {
|
||||
return new EulerTourEdge(newNode, max);
|
||||
}
|
||||
|
||||
/** Removes the specified edge from the Euler tour forest F_i. */
|
||||
/**
|
||||
* Removes the specified edge from the Euler tour forest F_i.
|
||||
*/
|
||||
private void removeForestEdge(EulerTourEdge edge) {
|
||||
EulerTourNode firstNode;
|
||||
EulerTourNode secondNode;
|
||||
@@ -535,8 +542,9 @@ public class ConnGraph {
|
||||
/**
|
||||
* Adds the specified edge to the edge map for srcInfo (srcInfo.edges). Assumes that the edge is not currently in
|
||||
* the map.
|
||||
* @param edge The edge.
|
||||
* @param srcInfo The source vertex's info.
|
||||
*
|
||||
* @param edge The edge.
|
||||
* @param srcInfo The source vertex's info.
|
||||
* @param destVertex The destination vertex, i.e. the edge's key in srcInfo.edges.
|
||||
*/
|
||||
private void addToEdgeMap(ConnEdge edge, VertexInfo srcInfo, ConnVertex destVertex) {
|
||||
@@ -549,6 +557,7 @@ public class ConnGraph {
|
||||
/**
|
||||
* Adds an edge between the specified vertices, if such an edge is not already present. Taken together with
|
||||
* removeEdge, this method takes O(log^2 N) amortized time with high probability.
|
||||
*
|
||||
* @return Whether there was no edge between the vertices.
|
||||
*/
|
||||
public boolean addEdge(ConnVertex connVertex1, ConnVertex connVertex2) {
|
||||
@@ -557,8 +566,8 @@ public class ConnGraph {
|
||||
}
|
||||
if (vertexInfo.size() >= MAX_VERTEX_COUNT - 1) {
|
||||
throw new RuntimeException(
|
||||
"Sorry, ConnGraph has too many vertices to perform this operation. ConnGraph does not support " +
|
||||
"storing more than ~2^30 vertices at a time.");
|
||||
"Sorry, ConnGraph has too many vertices to perform this operation. ConnGraph does not support " +
|
||||
"storing more than ~2^30 vertices at a time.");
|
||||
}
|
||||
VertexInfo info1 = ensureInfo(connVertex1);
|
||||
if (info1.edges.containsKey(connVertex2)) {
|
||||
@@ -614,7 +623,7 @@ public class ConnGraph {
|
||||
return;
|
||||
}
|
||||
EulerTourNode node;
|
||||
for (node = root; node.left.hasForestEdge; node = node.left);
|
||||
for (node = root; node.left.hasForestEdge; node = node.left) ;
|
||||
while (node != null) {
|
||||
EulerTourVertex vertex = node.vertex;
|
||||
ConnEdge edge = vertex.forestListHead;
|
||||
@@ -657,7 +666,7 @@ public class ConnGraph {
|
||||
|
||||
// Iterate to the next node with hasForestEdge == true, clearing hasForestEdge as we go
|
||||
if (node.right.hasForestEdge) {
|
||||
for (node = node.right; node.left.hasForestEdge; node = node.left);
|
||||
for (node = node.right; node.left.hasForestEdge; node = node.left) ;
|
||||
} else {
|
||||
node.hasForestEdge = false;
|
||||
while (node.parent != null && node.parent.right == node) {
|
||||
@@ -674,6 +683,7 @@ public class ConnGraph {
|
||||
* tree, where i is the level of the tree. This is a "replacement" edge because it replaces the edge that was
|
||||
* previously connecting the two trees. We push any level-i edges we encounter that do not connect to another tree
|
||||
* down to level i - 1, adding them to G_{i - 1}. This method assumes that root.hasForestEdge is false.
|
||||
*
|
||||
* @param root The root of the tree.
|
||||
* @return The replacement edge, or null if there is no replacement edge.
|
||||
*/
|
||||
@@ -683,7 +693,7 @@ public class ConnGraph {
|
||||
return null;
|
||||
}
|
||||
EulerTourNode node;
|
||||
for (node = root; node.left.hasGraphEdge; node = node.left);
|
||||
for (node = root; node.left.hasGraphEdge; node = node.left) ;
|
||||
while (node != null) {
|
||||
EulerTourVertex vertex = node.vertex;
|
||||
ConnEdge edge = vertex.graphListHead;
|
||||
@@ -767,7 +777,7 @@ public class ConnGraph {
|
||||
// Iterate to the next node with hasGraphEdge == true. Note that nodes' hasGraphEdge fields can change as we
|
||||
// push down edges.
|
||||
if (node.right.hasGraphEdge) {
|
||||
for (node = node.right; node.left.hasGraphEdge; node = node.left);
|
||||
for (node = node.right; node.left.hasGraphEdge; node = node.left) ;
|
||||
} else {
|
||||
while (node.parent != null && (node.parent.right == node || !node.parent.hasGraphEdge)) {
|
||||
node = node.parent;
|
||||
@@ -798,6 +808,7 @@ public class ConnGraph {
|
||||
/**
|
||||
* Removes the edge between the specified vertices, if there is such an edge. Taken together with addEdge, this
|
||||
* method takes O(log^2 N) amortized time with high probability.
|
||||
*
|
||||
* @return Whether there was an edge between the vertices.
|
||||
*/
|
||||
public boolean removeEdge(ConnVertex vertex1, ConnVertex vertex2) {
|
||||
@@ -912,7 +923,9 @@ public class ConnGraph {
|
||||
return info2 != null && info1.vertex.arbitraryVisit.root() == info2.vertex.arbitraryVisit.root();
|
||||
}
|
||||
|
||||
/** Returns the vertices that are directly adjacent to the specified vertex. */
|
||||
/**
|
||||
* Returns the vertices that are directly adjacent to the specified vertex.
|
||||
*/
|
||||
public Collection<ConnVertex> adjacentVertices(ConnVertex vertex) {
|
||||
VertexInfo info = vertexInfo.get(vertex);
|
||||
if (info != null) {
|
||||
@@ -925,12 +938,12 @@ public class ConnGraph {
|
||||
/**
|
||||
* Sets the augmentation associated with the specified vertex. This method takes O(log N) time with high
|
||||
* probability.
|
||||
*
|
||||
* <p>
|
||||
* Note that passing a null value for the second argument is not the same as removing the augmentation. For that,
|
||||
* you need to call removeVertexAugmentation.
|
||||
*
|
||||
* @return The augmentation that was previously associated with the vertex. Returns null if it did not have any
|
||||
* associated augmentation.
|
||||
* associated augmentation.
|
||||
*/
|
||||
public Object setVertexAugmentation(ConnVertex connVertex, Object vertexAugmentation) {
|
||||
assertIsAugmented();
|
||||
@@ -952,8 +965,9 @@ public class ConnGraph {
|
||||
/**
|
||||
* Removes any augmentation associated with the specified vertex. This method takes O(log N) time with high
|
||||
* probability.
|
||||
*
|
||||
* @return The augmentation that was previously associated with the vertex. Returns null if it did not have any
|
||||
* associated augmentation.
|
||||
* associated augmentation.
|
||||
*/
|
||||
public Object removeVertexAugmentation(ConnVertex connVertex) {
|
||||
assertIsAugmented();
|
||||
@@ -1127,7 +1141,7 @@ public class ConnGraph {
|
||||
private void optimizeGraphEdges() {
|
||||
for (VertexInfo info : vertexInfo.values()) {
|
||||
EulerTourVertex vertex;
|
||||
for (vertex = info.vertex; vertex.lowerVertex != null; vertex = vertex.lowerVertex);
|
||||
for (vertex = info.vertex; vertex.lowerVertex != null; vertex = vertex.lowerVertex) ;
|
||||
while (vertex != null) {
|
||||
EulerTourNode node = vertex.arbitraryVisit;
|
||||
ConnEdge edge = vertex.graphListHead;
|
||||
|
||||
@@ -2,9 +2,13 @@ package com.github.btrekkie.connectivity;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/** A vertex in a ConnGraph. See the comments for ConnGraph. */
|
||||
/**
|
||||
* A vertex in a ConnGraph. See the comments for ConnGraph.
|
||||
*/
|
||||
public class ConnVertex {
|
||||
/** The thread-local random number generator we use by default to set the "hash" field. */
|
||||
/**
|
||||
* The thread-local random number generator we use by default to set the "hash" field.
|
||||
*/
|
||||
private static final ThreadLocal<Random> random = new ThreadLocal<Random>() {
|
||||
@Override
|
||||
protected Random initialValue() {
|
||||
@@ -24,8 +28,9 @@ public class ConnVertex {
|
||||
|
||||
/**
|
||||
* Constructs a new ConnVertex.
|
||||
*
|
||||
* @param random The random number generator to use to produce a random hash code. ConnGraph relies on random hash
|
||||
* codes for its performance guarantees.
|
||||
* codes for its performance guarantees.
|
||||
*/
|
||||
public ConnVertex(Random random) {
|
||||
hash = random.nextInt();
|
||||
|
||||
@@ -7,13 +7,19 @@ import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
* ConnGraph.
|
||||
*/
|
||||
class EulerTourNode extends RedBlackNode<EulerTourNode> {
|
||||
/** The dummy leaf node. */
|
||||
/**
|
||||
* The dummy leaf node.
|
||||
*/
|
||||
public static final EulerTourNode LEAF = new EulerTourNode(null, null);
|
||||
|
||||
/** The vertex this node visits. */
|
||||
/**
|
||||
* The vertex this node visits.
|
||||
*/
|
||||
public final EulerTourVertex vertex;
|
||||
|
||||
/** The number of nodes in the subtree rooted at this node. */
|
||||
/**
|
||||
* The number of nodes in the subtree rooted at this node.
|
||||
*/
|
||||
public int size;
|
||||
|
||||
/**
|
||||
@@ -53,13 +59,15 @@ class EulerTourNode extends RedBlackNode<EulerTourNode> {
|
||||
this.augmentationFunc = augmentationFunc;
|
||||
}
|
||||
|
||||
/** Like augment(), but only updates the augmentation fields hasGraphEdge and hasForestEdge. */
|
||||
/**
|
||||
* Like augment(), but only updates the augmentation fields hasGraphEdge and hasForestEdge.
|
||||
*/
|
||||
public boolean augmentFlags() {
|
||||
boolean newHasGraphEdge =
|
||||
left.hasGraphEdge || right.hasGraphEdge || (vertex.arbitraryVisit == this && vertex.graphListHead != null);
|
||||
left.hasGraphEdge || right.hasGraphEdge || (vertex.arbitraryVisit == this && vertex.graphListHead != null);
|
||||
boolean newHasForestEdge =
|
||||
left.hasForestEdge || right.hasForestEdge ||
|
||||
(vertex.arbitraryVisit == this && vertex.forestListHead != null);
|
||||
left.hasForestEdge || right.hasForestEdge ||
|
||||
(vertex.arbitraryVisit == this && vertex.forestListHead != null);
|
||||
if (newHasGraphEdge == hasGraphEdge && newHasForestEdge == hasForestEdge) {
|
||||
return false;
|
||||
} else {
|
||||
|
||||
@@ -35,9 +35,13 @@ class EulerTourVertex {
|
||||
*/
|
||||
public ConnEdge forestListHead;
|
||||
|
||||
/** The augmentation associated with this vertex, if any. This is null instead if higherVertex != null. */
|
||||
/**
|
||||
* The augmentation associated with this vertex, if any. This is null instead if higherVertex != null.
|
||||
*/
|
||||
public Object augmentation;
|
||||
|
||||
/** Whether there is any augmentation associated with this vertex. This is false instead if higherVertex != null. */
|
||||
/**
|
||||
* Whether there is any augmentation associated with this vertex. This is false instead if higherVertex != null.
|
||||
*/
|
||||
public boolean hasAugmentation;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import java.util.Map;
|
||||
* a given graph, regardless of how many levels the vertex is in. See the comments for the implementation of ConnGraph.
|
||||
*/
|
||||
class VertexInfo {
|
||||
/** The representation of the vertex in the highest level. */
|
||||
/**
|
||||
* The representation of the vertex in the highest level.
|
||||
*/
|
||||
public EulerTourVertex vertex;
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
package com.github.btrekkie.red_black_node;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A node in a red-black tree ( https://en.wikipedia.org/wiki/Red%E2%80%93black_tree ). Compared to a class like Java's
|
||||
@@ -15,20 +11,20 @@ import java.util.Set;
|
||||
* clients to directly observe and manipulate the structure of the tree. This gives clients flexibility, although it
|
||||
* also enables them to violate the red-black or BST properties. The RedBlackNode class provides methods for performing
|
||||
* various standard operations, such as insertion and removal.
|
||||
*
|
||||
* <p>
|
||||
* Unlike most implementations of binary search trees, RedBlackNode supports arbitrary augmentation. By subclassing
|
||||
* RedBlackNode, clients can add arbitrary data and augmentation information to each node. For example, if we were to
|
||||
* use a RedBlackNode subclass to implement a sorted set, the subclass would have a field storing an element in the set.
|
||||
* If we wanted to keep track of the number of non-leaf nodes in each subtree, we would store this as a "size" field and
|
||||
* override augment() to update this field. All RedBlackNode methods (such as "insert" and remove()) call augment() as
|
||||
* necessary to correctly maintain the augmentation information, unless otherwise indicated.
|
||||
*
|
||||
* <p>
|
||||
* The values of the tree are stored in the non-leaf nodes. RedBlackNode does not support use cases where values must be
|
||||
* stored in the leaf nodes. It is recommended that all of the leaf nodes in a given tree be the same (black)
|
||||
* RedBlackNode instance, to save space. The root of an empty tree is a leaf node, as opposed to null.
|
||||
*
|
||||
* <p>
|
||||
* For reference, a red-black tree is a binary search tree satisfying the following properties:
|
||||
*
|
||||
* <p>
|
||||
* - Every node is colored red or black.
|
||||
* - The leaf nodes, which are dummy nodes that do not store any values, are colored black.
|
||||
* - The root is black.
|
||||
@@ -36,11 +32,13 @@ import java.util.Set;
|
||||
* - Every path from the root to a leaf contains the same number of black nodes.
|
||||
*
|
||||
* @param <N> The type of node in the tree. For example, we might have
|
||||
* "class FooNode<T> extends RedBlackNode<FooNode<T>>".
|
||||
* "class FooNode<T> extends RedBlackNode<FooNode<T>>".
|
||||
* @author Bill Jacobs
|
||||
*/
|
||||
public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Comparable<N> {
|
||||
/** A Comparator that compares Comparable elements using their natural order. */
|
||||
/**
|
||||
* A Comparator that compares Comparable elements using their natural order.
|
||||
*/
|
||||
private static final Comparator<Comparable<Object>> NATURAL_ORDER = new Comparator<Comparable<Object>>() {
|
||||
@Override
|
||||
public int compare(Comparable<Object> value1, Comparable<Object> value2) {
|
||||
@@ -48,16 +46,24 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
}
|
||||
};
|
||||
|
||||
/** The parent of this node, if any. "parent" is null if this is a leaf node. */
|
||||
/**
|
||||
* The parent of this node, if any. "parent" is null if this is a leaf node.
|
||||
*/
|
||||
public N parent;
|
||||
|
||||
/** The left child of this node. "left" is null if this is a leaf node. */
|
||||
/**
|
||||
* The left child of this node. "left" is null if this is a leaf node.
|
||||
*/
|
||||
public N left;
|
||||
|
||||
/** The right child of this node. "right" is null if this is a leaf node. */
|
||||
/**
|
||||
* The right child of this node. "right" is null if this is a leaf node.
|
||||
*/
|
||||
public N right;
|
||||
|
||||
/** Whether the node is colored red, as opposed to black. */
|
||||
/**
|
||||
* Whether the node is colored red, as opposed to black.
|
||||
*/
|
||||
public boolean isRed;
|
||||
|
||||
/**
|
||||
@@ -65,12 +71,12 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* example, if we augment each node by subtree size (the number of non-leaf nodes in the subtree), this method would
|
||||
* set the size field of this node to be equal to the size field of the left child plus the size field of the right
|
||||
* child plus one.
|
||||
*
|
||||
* <p>
|
||||
* "Augmentation information" is information that we can compute about a subtree rooted at some node, preferably
|
||||
* based only on the augmentation information in the node's two children and the information in the node. Examples
|
||||
* of augmentation information are the sum of the values in a subtree and the number of non-leaf nodes in a subtree.
|
||||
* Augmentation information may not depend on the colors of the nodes.
|
||||
*
|
||||
* <p>
|
||||
* This method returns whether the augmentation information in any of the ancestors of this node might have been
|
||||
* affected by changes in this subtree since the last call to augment(). In the usual case, where the augmentation
|
||||
* information depends only on the information in this node and the augmentation information in its immediate
|
||||
@@ -79,7 +85,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* calling augment() differed from the size field of the left child plus the size field of the right child plus one.
|
||||
* False positives are permitted. The return value is unspecified if we have not called augment() on this node
|
||||
* before.
|
||||
*
|
||||
* <p>
|
||||
* This method may assume that this is not a leaf node. It may not assume that the augmentation information stored
|
||||
* in any of the tree's nodes is correct. However, if the augmentation information stored in all of the node's
|
||||
* descendants is correct, then the augmentation information stored in this node must be correct after calling
|
||||
@@ -94,7 +100,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* of RedBlackNode. For example, if this stores the size of the subtree rooted at this node, this should throw a
|
||||
* RuntimeException if the size field of this is not equal to the size field of the left child plus the size field
|
||||
* of the right child plus one. Note that we may call this on a leaf node.
|
||||
*
|
||||
* <p>
|
||||
* assertSubtreeIsValid() calls assertNodeIsValid() on each node, or at least starts to do so until it detects a
|
||||
* problem. assertNodeIsValid() should assume the node is in a tree that satisfies all properties common to all
|
||||
* red-black trees, as assertSubtreeIsValid() is responsible for such checks. assertNodeIsValid() should be
|
||||
@@ -107,58 +113,68 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
|
||||
}
|
||||
|
||||
/** Returns whether this is a leaf node. */
|
||||
/**
|
||||
* Returns whether this is a leaf node.
|
||||
*/
|
||||
public boolean isLeaf() {
|
||||
return left == null;
|
||||
}
|
||||
|
||||
/** Returns the root of the tree that contains this node. */
|
||||
/**
|
||||
* Returns the root of the tree that contains this node.
|
||||
*/
|
||||
public N root() {
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
while (node.parent != null) {
|
||||
node = node.parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** Returns the first node in the subtree rooted at this node, if any. */
|
||||
/**
|
||||
* Returns the first node in the subtree rooted at this node, if any.
|
||||
*/
|
||||
public N min() {
|
||||
if (isLeaf()) {
|
||||
return null;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
while (!node.left.isLeaf()) {
|
||||
node = node.left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** Returns the last node in the subtree rooted at this node, if any. */
|
||||
/**
|
||||
* Returns the last node in the subtree rooted at this node, if any.
|
||||
*/
|
||||
public N max() {
|
||||
if (isLeaf()) {
|
||||
return null;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
while (!node.right.isLeaf()) {
|
||||
node = node.right;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** Returns the node immediately before this in the tree that contains this node, if any. */
|
||||
/**
|
||||
* Returns the node immediately before this in the tree that contains this node, if any.
|
||||
*/
|
||||
public N predecessor() {
|
||||
if (!left.isLeaf()) {
|
||||
N node;
|
||||
for (node = left; !node.right.isLeaf(); node = node.right);
|
||||
for (node = left; !node.right.isLeaf(); node = node.right) ;
|
||||
return node;
|
||||
} else if (parent == null) {
|
||||
return null;
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
while (node.parent != null && node.parent.left == node) {
|
||||
node = node.parent;
|
||||
}
|
||||
@@ -166,17 +182,19 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the node immediately after this in the tree that contains this node, if any. */
|
||||
/**
|
||||
* Returns the node immediately after this in the tree that contains this node, if any.
|
||||
*/
|
||||
public N successor() {
|
||||
if (!right.isLeaf()) {
|
||||
N node;
|
||||
for (node = right; !node.left.isLeaf(); node = node.left);
|
||||
for (node = right; !node.left.isLeaf(); node = node.left) ;
|
||||
return node;
|
||||
} else if (parent == null) {
|
||||
return null;
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
while (node.parent != null && node.parent.right == node) {
|
||||
node = node.parent;
|
||||
}
|
||||
@@ -188,6 +206,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Performs a left rotation about this node. This method assumes that !isLeaf() && !right.isLeaf(). It calls
|
||||
* augment() on this node and on its resulting parent. However, it does not call augment() on any of the resulting
|
||||
* parent's ancestors, because that is normally the responsibility of the caller.
|
||||
*
|
||||
* @return The return value from calling augment() on the resulting parent.
|
||||
*/
|
||||
public boolean rotateLeft() {
|
||||
@@ -197,7 +216,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
N newParent = right;
|
||||
right = newParent.left;
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
if (!right.isLeaf()) {
|
||||
right.parent = nThis;
|
||||
}
|
||||
@@ -219,6 +238,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Performs a right rotation about this node. This method assumes that !isLeaf() && !left.isLeaf(). It calls
|
||||
* augment() on this node and on its resulting parent. However, it does not call augment() on any of the resulting
|
||||
* parent's ancestors, because that is normally the responsibility of the caller.
|
||||
*
|
||||
* @return The return value from calling augment() on the resulting parent.
|
||||
*/
|
||||
public boolean rotateRight() {
|
||||
@@ -228,7 +248,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
N newParent = left;
|
||||
left = newParent.right;
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
if (!left.isLeaf()) {
|
||||
left.parent = nThis;
|
||||
}
|
||||
@@ -252,6 +272,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs
|
||||
* any rotations by calling rotateLeft() and rotateRight(). This method is more efficient than fixInsertion if
|
||||
* "augment" is false or augment() might return false.
|
||||
*
|
||||
* @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment().
|
||||
*/
|
||||
public void fixInsertionWithoutGettingRoot(boolean augment) {
|
||||
@@ -339,6 +360,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be
|
||||
* red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs
|
||||
* any rotations by calling rotateLeft() and rotateRight().
|
||||
*
|
||||
* @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment().
|
||||
* @return The root of the resulting tree.
|
||||
*/
|
||||
@@ -352,6 +374,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be
|
||||
* red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs
|
||||
* any rotations by calling rotateLeft() and rotateRight().
|
||||
*
|
||||
* @return The root of the resulting tree.
|
||||
*/
|
||||
public N fixInsertion() {
|
||||
@@ -359,27 +382,29 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
return root();
|
||||
}
|
||||
|
||||
/** Returns a Comparator that compares instances of N using their natural order, as in N.compareTo. */
|
||||
/**
|
||||
* Returns a Comparator that compares instances of N using their natural order, as in N.compareTo.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private Comparator<N> naturalOrder() {
|
||||
Comparator comparator = (Comparator)NATURAL_ORDER;
|
||||
return (Comparator<N>)comparator;
|
||||
Comparator comparator = (Comparator) NATURAL_ORDER;
|
||||
return (Comparator<N>) comparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the specified node into the tree rooted at this node. Assumes this is the root. We treat newNode as a
|
||||
* solitary node that does not belong to any tree, and we ignore its initial "parent", "left", "right", and isRed
|
||||
* fields.
|
||||
*
|
||||
* <p>
|
||||
* If it is not efficient or convenient to find the location for a node using a Comparator, then you should manually
|
||||
* add the node to the appropriate location, color it red, and call fixInsertion().
|
||||
*
|
||||
* @param newNode The node to insert.
|
||||
* @param newNode The node to insert.
|
||||
* @param allowDuplicates Whether to insert newNode if there is an equal node in the tree. To check whether we
|
||||
* inserted newNode, check whether newNode.parent is null and the return value differs from newNode.
|
||||
* @param comparator A comparator indicating where to put the node. If this is null, we use the nodes' natural
|
||||
* order, as in N.compareTo. If you are passing null, then you must override the compareTo method, because the
|
||||
* default implementation requires the nodes to already be in the same tree.
|
||||
* inserted newNode, check whether newNode.parent is null and the return value differs from newNode.
|
||||
* @param comparator A comparator indicating where to put the node. If this is null, we use the nodes' natural
|
||||
* order, as in N.compareTo. If you are passing null, then you must override the compareTo method, because the
|
||||
* default implementation requires the nodes to already be in the same tree.
|
||||
* @return The root of the resulting tree.
|
||||
*/
|
||||
public N insert(N newNode, boolean allowDuplicates, Comparator<? super N> comparator) {
|
||||
@@ -387,7 +412,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
throw new IllegalArgumentException("This is not the root of a tree");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
if (isLeaf()) {
|
||||
newNode.isRed = false;
|
||||
newNode.left = nThis;
|
||||
@@ -436,6 +461,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
/**
|
||||
* Moves this node to its successor's former position in the tree and vice versa, i.e. sets the "left", "right",
|
||||
* "parent", and isRed fields of each. This method assumes that this is not a leaf node.
|
||||
*
|
||||
* @return The node with which we swapped.
|
||||
*/
|
||||
private N swapWithSuccessor() {
|
||||
@@ -458,7 +484,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
isRed = oldReplacementIsRed;
|
||||
left = oldReplacementLeft;
|
||||
right = oldReplacementRight;
|
||||
@@ -575,7 +601,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Removes this node from the tree that contains it. The effect of this method on the fields of this node is
|
||||
* unspecified. This method assumes that this is not a leaf node. This method is more efficient than remove() if
|
||||
* augment() might return false.
|
||||
*
|
||||
* <p>
|
||||
* If the node has two children, we begin by moving the node's successor to its former position, by changing the
|
||||
* successor's "left", "right", "parent", and isRed fields.
|
||||
*/
|
||||
@@ -665,7 +691,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
/**
|
||||
* Removes this node from the tree that contains it. The effect of this method on the fields of this node is
|
||||
* unspecified. This method assumes that this is not a leaf node.
|
||||
*
|
||||
* <p>
|
||||
* If the node has two children, we begin by moving the node's successor to its former position, by changing the
|
||||
* successor's "left", "right", "parent", and isRed fields.
|
||||
*
|
||||
@@ -697,13 +723,14 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* "iterator", in iteration order. This method is responsible for setting the "left", "right", "parent", and isRed
|
||||
* fields of the nodes, and calling augment() as appropriate. It ignores the initial values of the "left", "right",
|
||||
* "parent", and isRed fields.
|
||||
*
|
||||
* @param iterator The nodes.
|
||||
* @param size The number of nodes.
|
||||
* @param height The "height" of the subtree's root node above the deepest leaf in the tree that contains it. Since
|
||||
* insertion fixup is slow if there are too many red nodes and deleteion fixup is slow if there are too few red
|
||||
* nodes, we compromise and have red nodes at every fourth level. We color a node red iff its "height" is equal
|
||||
* to 1 mod 4.
|
||||
* @param leaf The leaf node.
|
||||
* @param size The number of nodes.
|
||||
* @param height The "height" of the subtree's root node above the deepest leaf in the tree that contains it. Since
|
||||
* insertion fixup is slow if there are too many red nodes and deleteion fixup is slow if there are too few red
|
||||
* nodes, we compromise and have red nodes at every fourth level. We color a node red iff its "height" is equal
|
||||
* to 1 mod 4.
|
||||
* @param leaf The leaf node.
|
||||
* @return The root of the subtree.
|
||||
*/
|
||||
private static <N extends RedBlackNode<N>> N createTree(
|
||||
@@ -735,8 +762,9 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* method is responsible for setting the "left", "right", "parent", and isRed fields of the nodes (excluding
|
||||
* "leaf"), and calling augment() as appropriate. It ignores the initial values of the "left", "right", "parent",
|
||||
* and isRed fields.
|
||||
*
|
||||
* @param nodes The nodes.
|
||||
* @param leaf The leaf node.
|
||||
* @param leaf The leaf node.
|
||||
* @return The root of the tree.
|
||||
*/
|
||||
public static <N extends RedBlackNode<N>> N createTree(Collection<? extends N> nodes, N leaf) {
|
||||
@@ -762,7 +790,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* all of these nodes. This method destroys the trees rooted at "this" and "last". We treat "pivot" as a solitary
|
||||
* node that does not belong to any tree, and we ignore its initial "parent", "left", "right", and isRed fields.
|
||||
* This method assumes that this node and "last" are the roots of their respective trees.
|
||||
*
|
||||
* <p>
|
||||
* This method takes O(log N) time. It is more efficient than inserting "pivot" and then calling concatenate(last).
|
||||
* It is considerably more efficient than inserting "pivot" and all of the nodes in "last".
|
||||
*/
|
||||
@@ -783,7 +811,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
// Compute the black height of the trees
|
||||
int firstBlackHeight = 0;
|
||||
@SuppressWarnings("unchecked")
|
||||
N first = (N)this;
|
||||
N first = (N) this;
|
||||
for (N node = first; node != null; node = node.right) {
|
||||
if (!node.isRed) {
|
||||
firstBlackHeight++;
|
||||
@@ -868,7 +896,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
return last;
|
||||
} else if (last.isLeaf()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
return nThis;
|
||||
} else {
|
||||
N node = last.min();
|
||||
@@ -884,6 +912,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* destructive, meaning it does not preserve the original tree. It assumes that this node is the root and is in the
|
||||
* same tree as splitNode. It takes O(log N) time. It is considerably more efficient than removing all of the
|
||||
* nodes at or after splitNode and then creating a new tree from those nodes.
|
||||
*
|
||||
* @param The node at which to split the tree.
|
||||
* @return An array consisting of the resulting trees.
|
||||
*/
|
||||
@@ -929,7 +958,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
N first = null;
|
||||
N firstParent = null;
|
||||
N last = null;
|
||||
@@ -1082,7 +1111,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
last.augment();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
N[] result = (N[])Array.newInstance(getClass(), 2);
|
||||
N[] result = (N[]) Array.newInstance(getClass(), 2);
|
||||
result[0] = first;
|
||||
result[1] = last;
|
||||
return result;
|
||||
@@ -1092,7 +1121,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Returns the lowest common ancestor of this node and "other" - the node that is an ancestor of both and is not the
|
||||
* parent of a node that is an ancestor of both. Assumes that this is in the same tree as "other". Assumes that
|
||||
* neither "this" nor "other" is a leaf node. This method may return "this" or "other".
|
||||
*
|
||||
* <p>
|
||||
* Note that while it is possible to compute the lowest common ancestor in O(P) time, where P is the length of the
|
||||
* path from this node to "other", the "lca" method is not guaranteed to take O(P) time. If your application
|
||||
* requires this, then you should write your own lowest common ancestor method.
|
||||
@@ -1114,7 +1143,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
|
||||
// Go up to nodes of the same depth
|
||||
@SuppressWarnings("unchecked")
|
||||
N parent = (N)this;
|
||||
N parent = (N) this;
|
||||
N otherParent = other;
|
||||
if (depth <= otherDepth) {
|
||||
for (int i = otherDepth; i > depth; i--) {
|
||||
@@ -1142,10 +1171,10 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Returns an integer comparing the position of this node in the tree that contains it with that of "other". Returns
|
||||
* a negative number if this is earlier, a positive number if this is later, and 0 if this is at the same position.
|
||||
* Assumes that this is in the same tree as "other". Assumes that neither "this" nor "other" is a leaf node.
|
||||
*
|
||||
* <p>
|
||||
* The base class's implementation takes O(log N) time. If a RedBlackNode subclass stores a value used to order the
|
||||
* nodes, then it could override compareTo to compare the nodes' values, which would take O(1) time.
|
||||
*
|
||||
* <p>
|
||||
* Note that while it is possible to compare the positions of two nodes in O(P) time, where P is the length of the
|
||||
* path from this node to "other", the default implementation of compareTo is not guaranteed to take O(P) time. If
|
||||
* your application requires this, then you should write your own comparison method.
|
||||
@@ -1224,7 +1253,9 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
}
|
||||
}
|
||||
|
||||
/** Throws a RuntimeException if the RedBlackNode fields of this are not correct for a leaf node. */
|
||||
/**
|
||||
* Throws a RuntimeException if the RedBlackNode fields of this are not correct for a leaf node.
|
||||
*/
|
||||
private void assertIsValidLeaf() {
|
||||
if (left != null || right != null || parent != null || isRed) {
|
||||
throw new RuntimeException("A leaf node's \"left\", \"right\", \"parent\", or isRed field is incorrect");
|
||||
@@ -1234,14 +1265,15 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
/**
|
||||
* Throws a RuntimeException if the subtree rooted at this node does not satisfy the red-black properties, excluding
|
||||
* the requirement that the root be black, or it contains a repeated node other than a leaf node.
|
||||
*
|
||||
* @param blackHeight The required number of black nodes in each path from this to a leaf node, including this and
|
||||
* the leaf node.
|
||||
* @param visited The nodes we have reached thus far, other than leaf nodes. This method adds the non-leaf nodes in
|
||||
* the subtree rooted at this node to "visited".
|
||||
* the leaf node.
|
||||
* @param visited The nodes we have reached thus far, other than leaf nodes. This method adds the non-leaf nodes in
|
||||
* the subtree rooted at this node to "visited".
|
||||
*/
|
||||
private void assertSubtreeIsValidRedBlack(int blackHeight, Set<Reference<N>> visited) {
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
if (left == null || right == null) {
|
||||
assertIsValidLeaf();
|
||||
if (blackHeight != 1) {
|
||||
@@ -1276,7 +1308,9 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls assertNodeIsValid() on every node in the subtree rooted at this node. */
|
||||
/**
|
||||
* Calls assertNodeIsValid() on every node in the subtree rooted at this node.
|
||||
*/
|
||||
private void assertNodesAreValid() {
|
||||
assertNodeIsValid();
|
||||
if (left != null) {
|
||||
@@ -1304,7 +1338,7 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
Set<Reference<N>> nodes = new HashSet<Reference<N>>();
|
||||
int blackHeight = 0;
|
||||
@SuppressWarnings("unchecked")
|
||||
N node = (N)this;
|
||||
N node = (N) this;
|
||||
while (node != null) {
|
||||
if (!nodes.add(new Reference<N>(node))) {
|
||||
throw new RuntimeException("The tree contains a repeated non-leaf node");
|
||||
@@ -1333,14 +1367,15 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Throws a RuntimeException if the nodes in the subtree rooted at this node are not in the specified order or they
|
||||
* do not lie in the specified range. Assumes that the subtree rooted at this node is a valid binary tree, i.e. it
|
||||
* has no repeated nodes other than leaf nodes.
|
||||
*
|
||||
* @param comparator A comparator indicating how the nodes should be ordered.
|
||||
* @param start The lower limit for nodes in the subtree, if any.
|
||||
* @param end The upper limit for nodes in the subtree, if any.
|
||||
* @param start The lower limit for nodes in the subtree, if any.
|
||||
* @param end The upper limit for nodes in the subtree, if any.
|
||||
*/
|
||||
private void assertOrderIsValid(Comparator<? super N> comparator, N start, N end) {
|
||||
if (!isLeaf()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
N nThis = (N)this;
|
||||
N nThis = (N) this;
|
||||
if (start != null && comparator.compare(nThis, start) < 0) {
|
||||
throw new RuntimeException("The nodes are not ordered correctly");
|
||||
}
|
||||
@@ -1359,8 +1394,9 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> implements Compara
|
||||
* Assumes that this is a valid binary tree, i.e. there are no repeated nodes other than leaf nodes. This method is
|
||||
* useful for debugging. RedBlackNode subclasses may want to override assertSubtreeIsValid() to call
|
||||
* assertOrderIsValid.
|
||||
*
|
||||
* @param comparator A comparator indicating how the nodes should be ordered. If this is null, we use the nodes'
|
||||
* natural order, as in N.compareTo.
|
||||
* natural order, as in N.compareTo.
|
||||
*/
|
||||
public void assertOrderIsValid(Comparator<? super N> comparator) {
|
||||
if (comparator == null) {
|
||||
|
||||
@@ -5,10 +5,13 @@ package com.github.btrekkie.red_black_node;
|
||||
/**
|
||||
* Wraps a value using reference equality. In other words, two references are equal only if their values are the same
|
||||
* object instance, as in ==.
|
||||
*
|
||||
* @param <T> The type of value.
|
||||
*/
|
||||
class Reference<T> {
|
||||
/** The value this wraps. */
|
||||
/**
|
||||
* The value this wraps.
|
||||
*/
|
||||
private final T value;
|
||||
|
||||
public Reference(T value) {
|
||||
@@ -19,7 +22,7 @@ class Reference<T> {
|
||||
if (!(obj instanceof Reference)) {
|
||||
return false;
|
||||
}
|
||||
Reference<?> reference = (Reference<?>)obj;
|
||||
Reference<?> reference = (Reference<?>) obj;
|
||||
return value == reference.value;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ import java.util.Comparator;
|
||||
/* We implement an ArbitraryOrderCollection using a red-black tree. We order the nodes arbitrarily.
|
||||
*/
|
||||
public class ArbitraryOrderCollection {
|
||||
/** The Comparator for ordering ArbitraryOrderNodes. */
|
||||
/**
|
||||
* The Comparator for ordering ArbitraryOrderNodes.
|
||||
*/
|
||||
private static final Comparator<ArbitraryOrderNode> NODE_COMPARATOR = new Comparator<ArbitraryOrderNode>() {
|
||||
@Override
|
||||
public int compare(ArbitraryOrderNode node1, ArbitraryOrderNode node2) {
|
||||
@@ -19,10 +21,14 @@ public class ArbitraryOrderCollection {
|
||||
}
|
||||
};
|
||||
|
||||
/** The root node of the tree. */
|
||||
/**
|
||||
* The root node of the tree.
|
||||
*/
|
||||
private ArbitraryOrderNode root = new ArbitraryOrderNode();
|
||||
|
||||
/** Adds and returns a new value for ordering. */
|
||||
/**
|
||||
* Adds and returns a new value for ordering.
|
||||
*/
|
||||
public ArbitraryOrderValue createValue() {
|
||||
ArbitraryOrderNode node = new ArbitraryOrderNode();
|
||||
root = root.insert(node, true, NODE_COMPARATOR);
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.github.btrekkie.arbitrary_order_collection;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/** A node in an ArbitraryOrderCollection tree. See ArbitraryOrderCollection. */
|
||||
/**
|
||||
* A node in an ArbitraryOrderCollection tree. See ArbitraryOrderCollection.
|
||||
*/
|
||||
class ArbitraryOrderNode extends RedBlackNode<ArbitraryOrderNode> {
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ package com.github.btrekkie.arbitrary_order_collection;
|
||||
* compareTo.
|
||||
*/
|
||||
public class ArbitraryOrderValue implements Comparable<ArbitraryOrderValue> {
|
||||
/** The node that establishes this value's relative position. */
|
||||
/**
|
||||
* The node that establishes this value's relative position.
|
||||
*/
|
||||
final ArbitraryOrderNode node;
|
||||
|
||||
ArbitraryOrderValue(ArbitraryOrderNode node) {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
package com.github.btrekkie.arbitrary_order_collection.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderCollection;
|
||||
import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderValue;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderCollection;
|
||||
import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ArbitraryOrderCollectionTest {
|
||||
/** Tests ArbitraryOrderCollection. */
|
||||
/**
|
||||
* Tests ArbitraryOrderCollection.
|
||||
*/
|
||||
@Test
|
||||
public void test() {
|
||||
ArbitraryOrderCollection collection = new ArbitraryOrderCollection();
|
||||
|
||||
@@ -2,14 +2,18 @@ package com.github.btrekkie.connectivity.test;
|
||||
|
||||
import com.github.btrekkie.connectivity.Augmentation;
|
||||
|
||||
/** Stores two values: a sum and a maximum. Used for testing augmentation in ConnGraph. */
|
||||
/**
|
||||
* Stores two values: a sum and a maximum. Used for testing augmentation in ConnGraph.
|
||||
*/
|
||||
class SumAndMax {
|
||||
/** An Augmentation that combines two SumAndMaxes into one. */
|
||||
/**
|
||||
* An Augmentation that combines two SumAndMaxes into one.
|
||||
*/
|
||||
public static final Augmentation AUGMENTATION = new Augmentation() {
|
||||
@Override
|
||||
public Object combine(Object value1, Object value2) {
|
||||
SumAndMax sumAndMax1 = (SumAndMax)value1;
|
||||
SumAndMax sumAndMax2 = (SumAndMax)value2;
|
||||
SumAndMax sumAndMax1 = (SumAndMax) value1;
|
||||
SumAndMax sumAndMax2 = (SumAndMax) value2;
|
||||
return new SumAndMax(sumAndMax1.sum + sumAndMax2.sum, Math.max(sumAndMax1.max, sumAndMax2.max));
|
||||
}
|
||||
};
|
||||
@@ -28,7 +32,7 @@ class SumAndMax {
|
||||
if (!(obj instanceof SumAndMax)) {
|
||||
return false;
|
||||
}
|
||||
SumAndMax sumAndMax = (SumAndMax)obj;
|
||||
SumAndMax sumAndMax = (SumAndMax) obj;
|
||||
return sum == sumAndMax.sum && max == sumAndMax.max;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,21 @@ package com.github.btrekkie.interval_tree;
|
||||
* interval. Each node is augmented with the maximum ending value of an interval in the subtree rooted at the node.
|
||||
*/
|
||||
public class IntervalTree {
|
||||
/** The root node of the tree. */
|
||||
/**
|
||||
* The root node of the tree.
|
||||
*/
|
||||
private IntervalTreeNode root = IntervalTreeNode.LEAF;
|
||||
|
||||
/** Adds the specified interval to this. */
|
||||
/**
|
||||
* Adds the specified interval to this.
|
||||
*/
|
||||
public void addInterval(IntervalTreeInterval interval) {
|
||||
root = root.insert(new IntervalTreeNode(interval), true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified interval from this, if it is present.
|
||||
*
|
||||
* @param interval The interval.
|
||||
* @return Whether the interval was present.
|
||||
*/
|
||||
|
||||
@@ -4,10 +4,14 @@ package com.github.btrekkie.interval_tree;
|
||||
* An inclusive range of values [start, end]. Two intervals are equal if they have the same starting and ending values.
|
||||
*/
|
||||
public class IntervalTreeInterval {
|
||||
/** The smallest value in the range. */
|
||||
/**
|
||||
* The smallest value in the range.
|
||||
*/
|
||||
public final double start;
|
||||
|
||||
/** The largest value in the range. */
|
||||
/**
|
||||
* The largest value in the range.
|
||||
*/
|
||||
public final double end;
|
||||
|
||||
public IntervalTreeInterval(double start, double end) {
|
||||
@@ -22,7 +26,7 @@ public class IntervalTreeInterval {
|
||||
if (!(obj instanceof IntervalTreeInterval)) {
|
||||
return false;
|
||||
}
|
||||
IntervalTreeInterval interval = (IntervalTreeInterval)obj;
|
||||
IntervalTreeInterval interval = (IntervalTreeInterval) obj;
|
||||
return start == interval.start && end == interval.end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,19 @@ import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
* nodes as suggested in the comments for the implementation of IntervalTree.
|
||||
*/
|
||||
class IntervalTreeNode extends RedBlackNode<IntervalTreeNode> {
|
||||
/** The dummy leaf node. */
|
||||
/**
|
||||
* The dummy leaf node.
|
||||
*/
|
||||
public static final IntervalTreeNode LEAF = new IntervalTreeNode();
|
||||
|
||||
/** The interval stored in this node. */
|
||||
/**
|
||||
* The interval stored in this node.
|
||||
*/
|
||||
public IntervalTreeInterval interval;
|
||||
|
||||
/** The maximum ending value of an interval in the subtree rooted at this node. */
|
||||
/**
|
||||
* The maximum ending value of an interval in the subtree rooted at this node.
|
||||
*/
|
||||
public double maxEnd;
|
||||
|
||||
public IntervalTreeNode(IntervalTreeInterval interval) {
|
||||
@@ -21,7 +27,9 @@ class IntervalTreeNode extends RedBlackNode<IntervalTreeNode> {
|
||||
maxEnd = interval.end;
|
||||
}
|
||||
|
||||
/** Constructs a new dummy leaf node. */
|
||||
/**
|
||||
* Constructs a new dummy leaf node.
|
||||
*/
|
||||
private IntervalTreeNode() {
|
||||
interval = null;
|
||||
maxEnd = Double.NEGATIVE_INFINITY;
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package com.github.btrekkie.interval_tree.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.btrekkie.interval_tree.IntervalTree;
|
||||
import com.github.btrekkie.interval_tree.IntervalTreeInterval;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class IntervalTreeTest {
|
||||
/** Tests IntervalTree. */
|
||||
/**
|
||||
* Tests IntervalTree.
|
||||
*/
|
||||
@Test
|
||||
public void test() {
|
||||
IntervalTree tree = new IntervalTree();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.github.btrekkie.red_black_node.test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests RedBlackNode. Most of the testing for RedBlackNode takes place in TreeListTest, IntervalTreeTest,
|
||||
@@ -30,8 +30,9 @@ public class RedBlackNodeTest {
|
||||
/**
|
||||
* Returns whether the nodes in the subtree rooted at the specified node are ordered correctly, as in
|
||||
* TestRedBlackNode.assertOrderIsValid.
|
||||
*
|
||||
* @param comparator A comparator indicating how the nodes should be ordered. If this is null, we use the nodes'
|
||||
* natural ordering, as in TestRedBlackNode.compare.
|
||||
* natural ordering, as in TestRedBlackNode.compare.
|
||||
*/
|
||||
private boolean isOrderValid(TestRedBlackNode node, Comparator<TestRedBlackNode> comparator) {
|
||||
try {
|
||||
@@ -42,7 +43,9 @@ public class RedBlackNodeTest {
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests RedBlackNode.assertSubtreeIsValid() and RedBlackNode.assertOrderIsValid. */
|
||||
/**
|
||||
* Tests RedBlackNode.assertSubtreeIsValid() and RedBlackNode.assertOrderIsValid.
|
||||
*/
|
||||
@Test
|
||||
public void testAssertIsValid() {
|
||||
// Create a perfectly balanced tree of height 3
|
||||
@@ -129,12 +132,12 @@ public class RedBlackNodeTest {
|
||||
node2.value = 3;
|
||||
assertFalse(isOrderValid(node3, null));
|
||||
assertFalse(
|
||||
isOrderValid(node3, new Comparator<TestRedBlackNode>() {
|
||||
@Override
|
||||
public int compare(TestRedBlackNode node1, TestRedBlackNode node2) {
|
||||
return node1.value - node2.value;
|
||||
}
|
||||
}));
|
||||
isOrderValid(node3, new Comparator<TestRedBlackNode>() {
|
||||
@Override
|
||||
public int compare(TestRedBlackNode node1, TestRedBlackNode node2) {
|
||||
return node1.value - node2.value;
|
||||
}
|
||||
}));
|
||||
node3.value = 3;
|
||||
node2.value = 2;
|
||||
|
||||
@@ -161,11 +164,11 @@ public class RedBlackNodeTest {
|
||||
assertTrue(isOrderValid(node0, null));
|
||||
assertTrue(isOrderValid(TestRedBlackNode.LEAF, null));
|
||||
assertTrue(
|
||||
isOrderValid(node3, new Comparator<TestRedBlackNode>() {
|
||||
@Override
|
||||
public int compare(TestRedBlackNode node1, TestRedBlackNode node2) {
|
||||
return node1.value - node2.value;
|
||||
}
|
||||
}));
|
||||
isOrderValid(node3, new Comparator<TestRedBlackNode>() {
|
||||
@Override
|
||||
public int compare(TestRedBlackNode node1, TestRedBlackNode node2) {
|
||||
return node1.value - node2.value;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,32 @@ package com.github.btrekkie.red_black_node.test;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/** A RedBlackNode for RedBlackNodeTest. */
|
||||
/**
|
||||
* A RedBlackNode for RedBlackNodeTest.
|
||||
*/
|
||||
class TestRedBlackNode extends RedBlackNode<TestRedBlackNode> {
|
||||
/** The dummy leaf node. */
|
||||
/**
|
||||
* The dummy leaf node.
|
||||
*/
|
||||
public static final TestRedBlackNode LEAF = new TestRedBlackNode();
|
||||
|
||||
/** The value stored in this node. "value" is unspecified if this is a leaf node. */
|
||||
/**
|
||||
* The value stored in this node. "value" is unspecified if this is a leaf node.
|
||||
*/
|
||||
public int value;
|
||||
|
||||
/** Whether this node is considered valid, as in assertNodeIsValid(). */
|
||||
/**
|
||||
* Whether this node is considered valid, as in assertNodeIsValid().
|
||||
*/
|
||||
public boolean isValid = true;
|
||||
|
||||
public TestRedBlackNode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/** Constructs a new dummy leaf node. */
|
||||
/**
|
||||
* Constructs a new dummy leaf node.
|
||||
*/
|
||||
private TestRedBlackNode() {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package com.github.btrekkie.sub_array_min;
|
||||
|
||||
/** A list of integers. SubArrayMin provides the ability to quickly determine the minimum value in a given sublist. */
|
||||
/**
|
||||
* A list of integers. SubArrayMin provides the ability to quickly determine the minimum value in a given sublist.
|
||||
*/
|
||||
/* We implement SubArrayMin using a red-black tree augmented by subtree size and minimum value. Using the subtree size
|
||||
* augmentation, we can find the node at a given index.
|
||||
*/
|
||||
public class SubArrayMin {
|
||||
/** The root node. */
|
||||
/**
|
||||
* The root node.
|
||||
*/
|
||||
private SubArrayMinNode root = SubArrayMinNode.LEAF;
|
||||
|
||||
/** Appends the specified value to the end of the list. */
|
||||
/**
|
||||
* Appends the specified value to the end of the list.
|
||||
*/
|
||||
public void add(int value) {
|
||||
SubArrayMinNode newNode = new SubArrayMinNode(value);
|
||||
newNode.left = SubArrayMinNode.LEAF;
|
||||
@@ -25,7 +31,9 @@ public class SubArrayMin {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the node for the element with the specified index. Assumes "index" is in the range [0, root.size). */
|
||||
/**
|
||||
* Returns the node for the element with the specified index. Assumes "index" is in the range [0, root.size).
|
||||
*/
|
||||
private SubArrayMinNode getNode(int index) {
|
||||
if (index < 0 || index >= root.size) {
|
||||
throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + ")");
|
||||
|
||||
@@ -2,18 +2,28 @@ package com.github.btrekkie.sub_array_min;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/** A node in a SubArrayMin object. See the comments for the implementation of that class. */
|
||||
/**
|
||||
* A node in a SubArrayMin object. See the comments for the implementation of that class.
|
||||
*/
|
||||
class SubArrayMinNode extends RedBlackNode<SubArrayMinNode> {
|
||||
/** The dummy leaf node. */
|
||||
/**
|
||||
* The dummy leaf node.
|
||||
*/
|
||||
public static final SubArrayMinNode LEAF = new SubArrayMinNode();
|
||||
|
||||
/** The element stored in the node. The value is unspecified if this is a leaf node. */
|
||||
/**
|
||||
* The element stored in the node. The value is unspecified if this is a leaf node.
|
||||
*/
|
||||
public final int value;
|
||||
|
||||
/** The number of elements in the subtree rooted at this node. */
|
||||
/**
|
||||
* The number of elements in the subtree rooted at this node.
|
||||
*/
|
||||
public int size;
|
||||
|
||||
/** The minimum element in the subtree rooted at this node. This is Integer.MAX_VALUE if this is a leaf node. */
|
||||
/**
|
||||
* The minimum element in the subtree rooted at this node. This is Integer.MAX_VALUE if this is a leaf node.
|
||||
*/
|
||||
public int min;
|
||||
|
||||
public SubArrayMinNode(int value) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.github.btrekkie.sub_array_min.test;
|
||||
|
||||
import com.github.btrekkie.sub_array_min.SubArrayMin;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.btrekkie.sub_array_min.SubArrayMin;
|
||||
|
||||
public class SubArrayMinTest {
|
||||
/** Tests SubArrayMin. */
|
||||
/**
|
||||
* Tests SubArrayMin.
|
||||
*/
|
||||
@Test
|
||||
public void test() {
|
||||
SubArrayMin sam = new SubArrayMin();
|
||||
|
||||
Reference in New Issue
Block a user