Skip to content

Commit

Permalink
Spring clean
Browse files Browse the repository at this point in the history
  • Loading branch information
bfdes committed May 8, 2023
1 parent ab10e76 commit 62bcc2e
Show file tree
Hide file tree
Showing 17 changed files with 234 additions and 356 deletions.
36 changes: 17 additions & 19 deletions src/main/java/in/bfdes/collections/BinaryHeap.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,19 @@ public V peek() {
public V pop() {
if (isEmpty())
throw new IllegalStateException();
var smallestItem = values[1];
var value = values[1];

swap(1, size);
// Prevent loitering
keys[size] = null;
values[size] = null;
size--;
sink(1);
// Clear index
index.delete(smallestItem);
index.delete(value);

// Lazily reduces the size of the backing array, if necessary
if (size == keys.length / 4)
resize(keys.length / 2);

return smallestItem;
return value;
}

@Override
Expand Down Expand Up @@ -121,19 +118,23 @@ private void resize(int newCapacity) {
values = newValues;
}

private void sink(int k) {
while (2 * k <= size) {
var smallest = 2 * k < size && isGreater(2 * k, 2 * k + 1) ? 2 * k + 1 : 2 * k;
if (!isGreater(k, smallest)) break;
swap(k, smallest);
k = smallest;
private void sink(int i) {
while (2 * i <= size) {
var l = 2 * i;
var r = 2 * i + 1;

var m = l < size && isGreater(l, r) ? r : l;
if (!isGreater(i, m))
break;
swap(i, m);
i = m;
}
}

private void swim(int k) {
while (k > 1 && isGreater(k / 2, k)) {
swap(k / 2, k);
k = k / 2;
private void swim(int i) {
while (i > 1 && isGreater(i / 2, i)) {
swap(i / 2, i);
i = i / 2;
}
}

Expand All @@ -142,16 +143,13 @@ private boolean isGreater(int i, int j) {
}

private void swap(int i, int j) {
// Swap keys
var tmpKey = keys[i];
keys[i] = keys[j];
keys[j] = tmpKey;

// Maintain index
index.put(values[i], j);
index.put(values[j], i);

// Swap values
var tmpValue = values[i];
values[i] = values[j];
values[j] = tmpValue;
Expand Down
57 changes: 22 additions & 35 deletions src/main/java/in/bfdes/collections/BinarySearchTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,17 @@
import java.util.function.Function;

public class BinarySearchTree<K extends Comparable<K>, V> implements Tree<K, V> {
/*
Nodes of a binary tree can either be empty or contain left and right subtrees.
We say a binary tree is in symmetric order if the key of each node is
- larger than all the keys in its left subtree, and
- smaller than all the keys in its right subtree.
n.b. Our tree is not allowed to contain duplicate keys.
*/
private Node root;

private class Node {
public K key;
public V value;
public Node left, right;
public int size = 1; // size of the tree rooted at this node

/**
* We store `size` on the nodes rather than the `BinarySearchTree` in order to service efficient
* implementations of `rank`, `floor` and `ceiling`
/*
Size of the tree rooted at this node.
*/
public int size = 1;

public Node(K key, V value) {
this.key = key;
this.value = value;
Expand All @@ -36,6 +28,14 @@ public Node min() {
return node;
}

public Node deleteMin() {
if (left == null)
return right;
left = left.deleteMin();
size = 1 + size(left) + size(right);
return this;
}

public boolean isSymmetric() {
var isLeftSymmetric =
left == null || key.compareTo(left.key) > 0 && left.isSymmetric();
Expand All @@ -54,10 +54,10 @@ public void put(K key, V value) {
@Override
public Node apply(Node node) {
if (node == null)
return new Node(key, value); // k-v pair not found, so create one
return new Node(key, value);
var diff = key.compareTo(node.key);
if (diff == 0)
node.value = value; // existing k-v pair found, so overwrite its value
node.value = value;
else if (diff < 0)
node.left = apply(node.left);
else
Expand Down Expand Up @@ -105,17 +105,6 @@ public void delete(K key) {
if (key == null)
throw new IllegalArgumentException();

var deleteMin = new Function<Node, Node>() {
@Override
public Node apply(Node node) {
if (node.left == null)
return node.right;
node.left = apply(node.left);
node.size = 1 + size(node.left) + size(node.right);
return node;
}
};

var delete = new Function<Node, Node>() {
@Override
public Node apply(Node node) {
Expand All @@ -129,14 +118,12 @@ public Node apply(Node node) {
else if (diff > 0)
node.right = apply(node.right);
else {
// Found key
if (node.left == null) return node.right;
if (node.right == null) return node.left;
// Found two children
var min = node.right.min();
node.key = min.key;
node.value = min.value;
node.right = deleteMin.apply(node.right);
node.right = node.right.deleteMin();
}
node.size = 1 + size(node.left) + size(node.right);
return node;
Expand Down Expand Up @@ -167,7 +154,7 @@ public int size() {
}

@Override
public Iterator<Tuple<K, V>> iterator() {
public Iterator<Pair<K, V>> iterator() {
return new Iterator<>() {
private final List<Node> stack = new LinkedList<>();
private Node node = root;
Expand All @@ -178,15 +165,15 @@ public boolean hasNext() {
}

@Override
public Tuple<K, V> next() {
public Pair<K, V> next() {
if (!hasNext())
throw new NoSuchElementException();
while (node != null) {
stack.push(node);
node = node.left;
}
node = stack.pop();
var tuple = new Tuple<>(node.key, node.value);
var tuple = new Pair<>(node.key, node.value);
node = node.right;
return tuple;
}
Expand All @@ -196,7 +183,7 @@ public Tuple<K, V> next() {
@Override
public Iterable<K> keys() {
return () -> new Iterator<>() {
private final Iterator<Tuple<K, V>> items = iterator();
private final Iterator<Pair<K, V>> items = iterator();

@Override
public boolean hasNext() {
Expand All @@ -207,15 +194,15 @@ public boolean hasNext() {
public K next() {
if (!hasNext())
throw new NoSuchElementException();
return items.next().first();
return items.next().key();
}
};
}

@Override
public Iterable<V> values() {
return () -> new Iterator<>() {
private final Iterator<Tuple<K, V>> items = iterator();
private final Iterator<Pair<K, V>> items = iterator();

@Override
public boolean hasNext() {
Expand All @@ -226,7 +213,7 @@ public boolean hasNext() {
public V next() {
if (!hasNext())
throw new NoSuchElementException();
return items.next().second();
return items.next().value();
}
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/in/bfdes/collections/CircularBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public T pop() {
if (isEmpty())
throw new NoSuchElementException();
var item = buffer[head];
buffer[head] = null; // prevents loitering
buffer[head] = null;
head = (head + 1) % capacity();
size--;
return item;
Expand Down Expand Up @@ -86,7 +86,7 @@ public boolean hasNext() {
public T next() {
if (!hasNext())
throw new NoSuchElementException();
T item = buffer[next];
var item = buffer[next];
next = (next + 1) % capacity();
return item;
}
Expand Down
19 changes: 8 additions & 11 deletions src/main/java/in/bfdes/collections/DirectedGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public void add(V vertex) {
public void add(Edge<V> edge) {
if (edge == null)
throw new IllegalArgumentException();
// Add `from` and `to` to the graph, if they are not already present
var edges = vertices.getOrElse(edge.from(), new HashSet<>());
edges.add(edge);
vertices.put(edge.from(), edges);
Expand All @@ -46,10 +45,9 @@ public boolean contains(Edge<V> edge) {
public void remove(V vertex) {
if (vertex == null)
throw new IllegalArgumentException();
// Remove both incoming and outgoing edges for `vertex`, which requires a full scan
for (var edge : edges())
if (edge.to().equals(vertex))
remove(edge); // Note: `edge.from` may be left without any edges
remove(edge);
vertices.delete(vertex);
}

Expand All @@ -58,7 +56,7 @@ public void remove(Edge<V> edge) {
if (edge == null)
throw new IllegalArgumentException();
var edges = vertices.get(edge.from());
edges.remove(edge); // Note: `from` and `to` may be left without any edges
edges.remove(edge);
}

@Override
Expand All @@ -73,12 +71,11 @@ public int size() {

@Override
public Iterable<V> vertices() {
return vertices(VertexOrder.ANY_ORDER);
return vertices.keys();
}

@Override
public Iterable<Edge<V>> edges() {
// TODO Stream edges without copying all of them first
var edges = new LinkedList<Edge<V>>();
for (var vertex : vertices())
for (var edge : edges(vertex))
Expand Down Expand Up @@ -106,17 +103,17 @@ public DirectedGraph<V> transpose() {
*/
public boolean isCyclic() {
// Use recursive DFS to determine whether a graph is cyclic.
// In a depth-first trace of a cyclic graph at least one vertex will appear twice.
// In a depth-key trace of a cyclic graph at least one vertex will appear twice.
var visitedVertices = new HashSet<V>();
var stack = new HashSet<V>();

var isCyclic = new Predicate<V>() {
@Override
public boolean test(V vertex) {
if (stack.contains(vertex))
return true; // `vertex` encountered twice in a depth-first trace
return true; // `vertex` encountered twice in a depth-key trace
if (visitedVertices.contains(vertex))
return false; // `vertex` was processed in some other depth-first trace
return false; // `vertex` was processed in some other depth-key trace
visitedVertices.add(vertex);
stack.add(vertex);

Expand Down Expand Up @@ -163,7 +160,7 @@ public void accept(V vertex) {
/*
Invoke `collect` on all vertices to account for the fact that:
1. Not all vertices are reachable from one another
2. The first vertex we call `collect` on might have incoming links
2. The key vertex we call `collect` on might have incoming links
*/
for (var vertex : vertices.keys())
if (!visitedVertices.contains(vertex))
Expand Down Expand Up @@ -284,7 +281,7 @@ public Paths<V> shortestPaths(V source, Strategy strategy) {
yield new Paths<>(source, incomingEdges);
}
case DIJKSTRA -> {
var adjacentVertices = new BinaryHeap<V, Double>();
var adjacentVertices = new BinaryHeap<V, Double>();
adjacentVertices.push(source, 0d);

Consumer<Edge<V>> relax = edge -> {
Expand Down
9 changes: 0 additions & 9 deletions src/main/java/in/bfdes/collections/Graph.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package in.bfdes.collections;

public interface Graph<V> {
/**
* Adds a vertex to the graph, if it does not already exist.
*/
void add(V vertex);

void add(Edge<V> edge);
Expand All @@ -16,14 +13,8 @@ public interface Graph<V> {

void remove(Edge<V> edge);

/**
* Returns true if this graph contains no vertices, false otherwise.
*/
boolean isEmpty();

/**
* Returns the number of vertices in this graph.
*/
int size();

Iterable<V> vertices();
Expand Down

0 comments on commit 62bcc2e

Please sign in to comment.