diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyList.java b/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyList.java index c8f0d36df..41b8535af 100644 --- a/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyList.java +++ b/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyList.java @@ -1,205 +1,199 @@ /** - * This file shows you how to determine if a graph is bipartite or not. This can be achieved in - * linear time by coloring the visited nodes. + * Bipartite Graph Check — Adjacency List (DFS coloring) * - *
Time Complexity: O(V + E)
+ * Determines if an undirected graph is bipartite (2-colorable) by attempting
+ * to color it with two colors via DFS. A graph is bipartite if and only if
+ * it contains no odd-length cycles.
+ *
+ * The algorithm starts a DFS from node 0, alternating colors RED and BLACK.
+ * If a neighbor already has the same color as the current node, the graph
+ * is not bipartite.
+ *
+ * Note: this implementation only checks the connected component containing
+ * node 0. Disconnected graphs with multiple components are reported as
+ * not bipartite.
+ *
+ * Time: O(V + E)
+ * Space: O(V)
*
* @author William Fiset, william.alexandre.fiset@gmail.com
*/
package com.williamfiset.algorithms.graphtheory.networkflow;
-import com.williamfiset.algorithms.utils.graphutils.Utils;
+import java.util.ArrayList;
import java.util.List;
public class BipartiteGraphCheckAdjacencyList {
- private int n;
+ // Color constants. XOR with 0b01 toggles between RED and BLACK:
+ // RED ^ 1 = BLACK, BLACK ^ 1 = RED.
+ public static final int UNVISITED = 0, RED = 0b10, BLACK = 0b11;
+
+ private final int n;
+ private final List Time Complexity: O(fE), where f is the max flow and E is the number of edges
+ * The Ford-Fulkerson method finds the maximum flow in a flow network by
+ * repeatedly finding augmenting paths from source to sink and pushing flow
+ * along them. This implementation uses DFS to find each augmenting path.
+ *
+ * Algorithm:
+ * The min-cut is obtained as a byproduct: after the algorithm terminates,
+ * all nodes still reachable from the source in the residual graph belong to
+ * the source side of the minimum cut.
+ *
+ * Time Complexity: O(fE), where f is the max flow and E is the number of edges.
+ * The DFS-based approach can be slow on graphs with large integer capacities
+ * because each augmenting path may only push one unit of flow.
*
* @author William Fiset, william.alexandre.fiset@gmail.com
*/
@@ -16,8 +32,8 @@
public class FordFulkersonDfsSolverAdjacencyList extends NetworkFlowSolverBase {
/**
- * Creates an instance of a flow network solver. Use the {@link #addEdge(int, int, int)} method to
- * add edges to the graph.
+ * Creates an instance of a flow network solver. Use the {@link #addEdge} method to add edges to
+ * the graph.
*
* @param n - The number of nodes in the graph including source and sink nodes.
* @param s - The index of the source node, 0 <= s < n
@@ -27,34 +43,33 @@ public FordFulkersonDfsSolverAdjacencyList(int n, int s, int t) {
super(n, s, t);
}
- // Performs the Ford-Fulkerson method applying a depth first search as
- // a means of finding an augmenting path.
@Override
public void solve() {
-
- // Find max flow by adding all augmenting path flows.
+ // Repeatedly find augmenting paths via DFS and accumulate flow.
for (long f = dfs(s, INF); f != 0; f = dfs(s, INF)) {
markAllNodesAsUnvisited();
maxFlow += f;
}
- // Find min cut.
- for (int i = 0; i < n; i++) if (visited(i)) minCut[i] = true;
+ // Nodes still reachable from source in the residual graph form the min-cut.
+ for (int i = 0; i < n; i++) {
+ if (visited(i)) {
+ minCut[i] = true;
+ }
+ }
}
private long dfs(int node, long flow) {
- // At sink node, return augmented path flow.
- if (node == t) return flow;
+ if (node == t) {
+ return flow;
+ }
- List Time complexity: O(V+E)
+ * The center of a tree is the set of nodes that minimizes the maximum
+ * distance (eccentricity) to any other node. A tree has either 1 center
+ * (odd diameter) or 2 adjacent centers (even diameter).
+ *
+ * The algorithm works by iteratively peeling leaf nodes layer by layer
+ * (like peeling an onion) until only the center node(s) remain. Each
+ * round removes all current leaves and decrements the degree of their
+ * neighbors, creating a new set of leaves for the next round.
+ *
+ * Time: O(V + E)
+ * Space: O(V)
*
* @author Original author: Jeffrey Xiao, https://github.com/jeffrey-xiao
- * @author Modifications by: William Fiset, william.alexandre.fiset@gmail.com
*/
package com.williamfiset.algorithms.graphtheory.treealgorithms;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
public class TreeCenter {
+ /**
+ * Returns the center node(s) of the tree. The result contains either
+ * 1 node (odd diameter) or 2 adjacent nodes (even diameter).
+ */
public static List http://webhome.cs.uvic.ca/~wendym/courses/582/16/notes/582_12_tree_can_form.pdf
- *
- * This implementation uses a breadth first search on an undirected graph to generate the tree's
- * canonical encoding.
- *
- * Tested code against: https://uva.onlinejudge.org/external/124/p12489.pdf
- *
- * @author William Fiset, william.alexandre.fiset@gmail.com
- */
-package com.williamfiset.algorithms.graphtheory.treealgorithms;
-
-import java.util.*;
-
-public class TreeIsomorphismWithBfs {
-
- public static List> graph;
private int[] colors;
private boolean solved;
private boolean isBipartite;
- private List
> graph;
-
- @SuppressWarnings("XorPower")
- public static final int RED = 0b10, BLACK = (RED ^ 1);
public BipartiteGraphCheckAdjacencyList(List
> graph) {
if (graph == null) throw new IllegalArgumentException("Graph cannot be null.");
- n = graph.size();
+ this.n = graph.size();
this.graph = graph;
}
- // Checks whether the input graph is bipartite.
+ /** Returns true if the graph is bipartite (2-colorable). */
public boolean isBipartite() {
if (!solved) solve();
return isBipartite;
}
- // If the input graph is bipartite it has a two coloring which can be obtained
- // through this method. Each index in the returned array is either RED or BLACK
- // indicating which color node i was colored.
+ /**
+ * Returns the two-coloring array if the graph is bipartite, null otherwise.
+ * Each entry is either RED or BLACK.
+ */
public int[] getTwoColoring() {
return isBipartite() ? colors : null;
}
private void solve() {
if (n <= 1) return;
-
colors = new int[n];
- int nodesVisited = colorGraph(0, RED);
-
- // The graph is not bipartite. Either not all the nodes were visited or the
- // colorGraph method returned -1 meaning the graph is not 2-colorable.
- isBipartite = (nodesVisited == n);
+ isBipartite = colorGraph(0, RED) && allNodesVisited();
solved = true;
}
- // Do a depth first search coloring the nodes of the graph as we go.
- // This method returns the count of the number of nodes visited while
- // coloring the graph or -1 if this graph is not bipartite.
- private int colorGraph(int i, int color) {
- colors[i] = color;
-
- // Toggles the color between RED and BLACK by exploiting the binary representation
- // of the constants and flipping the least significant bit on and off.
- int nextColor = (color ^ 1);
-
- int visitCount = 1;
- List
> createGraph(int n) {
+ List
> graph = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
+ return graph;
+ }
- return visitCount;
+ public static void addUndirectedEdge(List
> graph, int from, int to) {
+ graph.get(from).add(to);
+ graph.get(to).add(from);
}
- /* Example usage */
+ // ==================== Main ====================
public static void main(String[] args) {
+ testSelfLoop();
+ testTwoNodes();
+ testTriangle();
+ testDisjoint();
+ testSquare();
+ testSquareWithDiagonal();
+ }
- // Singleton (not bipartite)
- int n = 1;
- List
> graph = Utils.createEmptyAdjacencyList(n);
- Utils.addUndirectedEdge(graph, 0, 0);
- displayGraph(graph);
-
- // Prints:
- // Graph has 1 node(s) and the following edges:
- // 0 -> 0
- // 0 -> 0
- // This graph is bipartite: false
-
- // Two nodes one edge between them (bipartite)
- n = 2;
- graph = Utils.createEmptyAdjacencyList(n);
- Utils.addUndirectedEdge(graph, 0, 1);
- displayGraph(graph);
-
- // Prints:
- // Graph has 2 node(s) and the following edges:
- // 0 -> 1
- // 1 -> 0
- // This graph is bipartite: true
-
- // Triangle graph (not bipartite)
- n = 3;
- graph = Utils.createEmptyAdjacencyList(n);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 1, 2);
- Utils.addUndirectedEdge(graph, 2, 0);
- displayGraph(graph);
-
- // Prints:
- // Graph has 3 node(s) and the following edges:
- // 0 -> 1
- // 0 -> 2
- // 1 -> 0
- // 1 -> 2
- // 2 -> 1
- // 2 -> 0
- // This graph is bipartite: false
-
- // Disjoint graph is bipartite connected components (altogether not bipartite)
- n = 4;
- graph = Utils.createEmptyAdjacencyList(n);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 2, 3);
- displayGraph(graph);
-
- // Prints:
- // Graph has 4 node(s) and the following edges:
- // 0 -> 1
- // 1 -> 0
- // 2 -> 3
- // 3 -> 2
- // This graph is bipartite: false
-
- // Square graph (bipartite)
- n = 4;
- graph = Utils.createEmptyAdjacencyList(n);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 1, 2);
- Utils.addUndirectedEdge(graph, 2, 3);
- Utils.addUndirectedEdge(graph, 3, 0);
- displayGraph(graph);
-
- // Prints:
- // Graph has 4 node(s) and the following edges:
- // 0 -> 1
- // 0 -> 3
- // 1 -> 0
- // 1 -> 2
- // 2 -> 1
- // 2 -> 3
- // 3 -> 2
- // 3 -> 0
- // This graph is bipartite: true
-
- // Square graph with additional edge (not bipartite)
- n = 4;
- graph = Utils.createEmptyAdjacencyList(n);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 1, 2);
- Utils.addUndirectedEdge(graph, 2, 3);
- Utils.addUndirectedEdge(graph, 3, 0);
- Utils.addUndirectedEdge(graph, 0, 2);
- displayGraph(graph);
-
- // Prints:
- // Graph has 4 node(s) and the following edges:
- // 0 -> 1
- // 0 -> 3
- // 0 -> 2
- // 1 -> 0
- // 1 -> 2
- // 2 -> 1
- // 2 -> 3
- // 2 -> 0
- // 3 -> 2
- // 3 -> 0
- // This graph is bipartite: false
+ //
+ // +-+
+ // |0| (self-loop)
+ // +-+
+ //
+ // Not bipartite: node 0 is adjacent to itself.
+ //
+ private static void testSelfLoop() {
+ List
> g = createGraph(1);
+ addUndirectedEdge(g, 0, 0);
+ System.out.println(new BipartiteGraphCheckAdjacencyList(g).isBipartite()); // false
+ }
+ //
+ // R --- B
+ // 0 1
+ //
+ // Bipartite: {0=RED, 1=BLACK}
+ //
+ private static void testTwoNodes() {
+ List
> g = createGraph(2);
+ addUndirectedEdge(g, 0, 1);
+ System.out.println(new BipartiteGraphCheckAdjacencyList(g).isBipartite()); // true
}
- private static void displayGraph(List
> graph) {
- final int n = graph.size();
+ //
+ // R --- B
+ // 0 1
+ // \ /
+ // 2
+ // ? ← must be both R and B
+ //
+ // Not bipartite: odd cycle (0-1-2).
+ //
+ private static void testTriangle() {
+ List
> g = createGraph(3);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 1, 2);
+ addUndirectedEdge(g, 2, 0);
+ System.out.println(new BipartiteGraphCheckAdjacencyList(g).isBipartite()); // false
+ }
- System.out.println("Graph has " + n + " node(s) and the following edges:");
- for (int f = 0; f < n; f++) for (int t : graph.get(f)) System.out.println(f + " -> " + t);
+ //
+ // R --- B ? --- ?
+ // 0 1 2 3
+ //
+ // Not bipartite: nodes 2,3 unreachable from node 0.
+ //
+ private static void testDisjoint() {
+ List
> g = createGraph(4);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 2, 3);
+ System.out.println(new BipartiteGraphCheckAdjacencyList(g).isBipartite()); // false
+ }
- BipartiteGraphCheckAdjacencyList solver;
- solver = new BipartiteGraphCheckAdjacencyList(graph);
+ //
+ // R --- B
+ // 0 1
+ // | |
+ // 3 2
+ // B --- R
+ //
+ // Bipartite: {0=RED, 1=BLACK, 2=RED, 3=BLACK}
+ //
+ private static void testSquare() {
+ List
> g = createGraph(4);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 1, 2);
+ addUndirectedEdge(g, 2, 3);
+ addUndirectedEdge(g, 3, 0);
+ System.out.println(new BipartiteGraphCheckAdjacencyList(g).isBipartite()); // true
+ }
- System.out.println("This graph is bipartite: " + (solver.isBipartite()));
- System.out.println();
+ //
+ // R --- B
+ // 0 1
+ // | \ |
+ // | \ |
+ // 3 2
+ // B ? ← must be both R and B
+ //
+ // Not bipartite: diagonal 0-2 creates odd cycle (0-1-2).
+ //
+ private static void testSquareWithDiagonal() {
+ List
> g = createGraph(4);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 1, 2);
+ addUndirectedEdge(g, 2, 3);
+ addUndirectedEdge(g, 3, 0);
+ addUndirectedEdge(g, 0, 2);
+ System.out.println(new BipartiteGraphCheckAdjacencyList(g).isBipartite()); // false
}
}
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/FordFulkersonDfsSolverAdjacencyList.java b/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/FordFulkersonDfsSolverAdjacencyList.java
index 52c41bdf6..68022982f 100644
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/FordFulkersonDfsSolverAdjacencyList.java
+++ b/src/main/java/com/williamfiset/algorithms/graphtheory/networkflow/FordFulkersonDfsSolverAdjacencyList.java
@@ -1,9 +1,25 @@
/**
- * An implementation of the Ford-Fulkerson (FF) method with a DFS as a method of finding augmenting
- * paths. FF allows you to find the max flow through a directed graph and the min cut as a
- * byproduct.
+ * Ford-Fulkerson Max Flow — DFS Augmenting Paths (Adjacency List)
*
- *
+ *
+ *
+ * > graph, int rootId) {
- TreeNode root = new TreeNode(rootId);
- return buildTree(graph, root);
- }
-
- // Do dfs to construct rooted tree.
- private static TreeNode buildTree(List
> graph, TreeNode node) {
- int subtreeNodeCount = 1;
- for (int neighbor : graph.get(node.id())) {
- // Ignore adding an edge pointing back to parent.
- if (node.parent() != null && neighbor == node.parent().id()) {
- continue;
- }
-
- TreeNode child = new TreeNode(neighbor, node);
- node.addChildren(child);
-
- buildTree(graph, child);
- subtreeNodeCount += child.size();
- }
- node.setSize(subtreeNodeCount);
- return node;
- }
-
- @Override
- public String toString() {
- return String.valueOf(id);
- }
- }
-
- private TreeNode lcaNode = null;
- private TreeNode root;
-
- public LowestCommonAncestor(TreeNode root) {
- this.root = root;
- }
-
- // Finds the lowest common ancestor of the nodes with id1 and id2.
- public TreeNode lca(int id1, int id2) {
- lcaNode = null;
- helper(root, id1, id2);
- return lcaNode;
- }
-
- private boolean helper(TreeNode node, int id1, int id2) {
- if (node == null) {
- return false;
- }
- int count = 0;
- if (node.id() == id1) {
- count++;
- }
- if (node.id() == id2) {
- count++;
- }
- for (TreeNode child : node.children()) {
- if (helper(child, id1, id2)) {
- count++;
- }
- }
- if (count == 2) {
- lcaNode = node;
- }
- return count > 0;
- }
-
- /* Graph/Tree creation helper methods. */
-
- // Create a graph as a adjacency list with 'n' nodes.
- public static List
> createEmptyGraph(int n) {
- List
> graph = new ArrayList<>(n);
- for (int i = 0; i < n; i++) graph.add(new LinkedList<>());
- return graph;
- }
-
- public static void addUndirectedEdge(List
> graph, int from, int to) {
- graph.get(from).add(to);
- graph.get(to).add(from);
- }
-
- public static void main(String[] args) {
- TreeNode root = createFirstTreeFromSlides();
- LowestCommonAncestor solver = new LowestCommonAncestor(root);
- System.out.println(solver.lca(10, 15).id());
- }
-
- private static TreeNode createFirstTreeFromSlides() {
- int n = 17;
- List
> tree = createEmptyGraph(n);
-
- addUndirectedEdge(tree, 0, 1);
- addUndirectedEdge(tree, 0, 2);
- addUndirectedEdge(tree, 1, 3);
- addUndirectedEdge(tree, 1, 4);
- addUndirectedEdge(tree, 2, 5);
- addUndirectedEdge(tree, 2, 6);
- addUndirectedEdge(tree, 2, 7);
- addUndirectedEdge(tree, 3, 8);
- addUndirectedEdge(tree, 3, 9);
- addUndirectedEdge(tree, 5, 10);
- addUndirectedEdge(tree, 5, 11);
- addUndirectedEdge(tree, 7, 12);
- addUndirectedEdge(tree, 7, 13);
- addUndirectedEdge(tree, 11, 14);
- addUndirectedEdge(tree, 11, 15);
- addUndirectedEdge(tree, 11, 16);
-
- return TreeNode.rootTree(tree, 0);
- }
-}
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenter.java b/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenter.java
index 132b32ea0..5eb60093c 100644
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenter.java
+++ b/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenter.java
@@ -1,61 +1,63 @@
/**
- * This algorithm finds the center(s) of a tree.
+ * Tree Center(s)
*
- *
> tree) {
final int n = tree.size();
int[] degree = new int[n];
-
- // Find all leaf nodes
List
> createEmptyTree(int n) {
List
> tree = new ArrayList<>(n);
- for (int i = 0; i < n; i++) tree.add(new LinkedList<>());
+ for (int i = 0; i < n; i++) tree.add(new ArrayList<>());
return tree;
}
@@ -64,8 +66,18 @@ public static void addUndirectedEdge(List
> tree, int from, int to)
tree.get(to).add(from);
}
+ // ==================== Main ====================
+
public static void main(String[] args) {
+ // 0 - 1 - 2 - 3 - 4
+ // | |
+ // 6 5
+ // / \
+ // 7 8
+ //
+ // Center: [2]
+
List
> graph = createEmptyTree(9);
addUndirectedEdge(graph, 0, 1);
addUndirectedEdge(graph, 2, 1);
@@ -76,39 +88,6 @@ public static void main(String[] args) {
addUndirectedEdge(graph, 6, 7);
addUndirectedEdge(graph, 6, 8);
- // Centers are 2
- System.out.println(findTreeCenters(graph));
-
- // Centers are 0
- List
> graph2 = createEmptyTree(1);
- System.out.println(findTreeCenters(graph2));
-
- // Centers are 0,1
- List
> graph3 = createEmptyTree(2);
- addUndirectedEdge(graph3, 0, 1);
- System.out.println(findTreeCenters(graph3));
-
- // Centers are 1
- List
> graph4 = createEmptyTree(3);
- addUndirectedEdge(graph4, 0, 1);
- addUndirectedEdge(graph4, 1, 2);
- System.out.println(findTreeCenters(graph4));
-
- // Centers are 1,2
- List
> graph5 = createEmptyTree(4);
- addUndirectedEdge(graph5, 0, 1);
- addUndirectedEdge(graph5, 1, 2);
- addUndirectedEdge(graph5, 2, 3);
- System.out.println(findTreeCenters(graph5));
-
- // Centers are 2,3
- List
> graph6 = createEmptyTree(7);
- addUndirectedEdge(graph6, 0, 1);
- addUndirectedEdge(graph6, 1, 2);
- addUndirectedEdge(graph6, 2, 3);
- addUndirectedEdge(graph6, 3, 4);
- addUndirectedEdge(graph6, 4, 5);
- addUndirectedEdge(graph6, 4, 6);
- System.out.println(findTreeCenters(graph6));
+ System.out.println(findTreeCenters(graph)); // [2]
}
}
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismWithBfs.java b/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismWithBfs.java
deleted file mode 100644
index 7f3ccd01d..000000000
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismWithBfs.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * The graph isomorphism problem for general graphs can be quite difficult, however there exists an
- * elegant solution to uniquely encode a graph if it is a tree. Here is a brilliant explanation with
- * animations:
- *
- *
> createEmptyTree(int n) {
- List
> tree = new ArrayList<>(n);
- for (int i = 0; i < n; i++) tree.add(new ArrayList<>());
- return tree;
- }
-
- public static void addUndirectedEdge(List
> tree, int from, int to) {
- tree.get(from).add(to);
- tree.get(to).add(from);
- }
-
- private static List
> tree) {
- final int n = tree.size();
- int[] degrees = new int[n];
-
- // Find all leaf nodes
- List
> tree) {
- if (tree == null || tree.size() == 0) return "";
- if (tree.size() == 1) return "()";
- final int n = tree.size();
-
- int root = findTreeCenters(tree).get(0);
-
- int[] degree = new int[n];
- int[] parent = new int[n];
- boolean[] visited = new boolean[n];
- List
> tree1, List
> tree2) {
- return encodeTree(tree1).equals(encodeTree(tree2));
- }
-
- /* Example usage */
-
- public static void main(String[] args) {
- // Test if two tree are isomorphic, meaning they are structurally equivalent
- // but are labeled differently.
- List
> tree1 = createEmptyTree(5);
- List
> tree2 = createEmptyTree(5);
-
- addUndirectedEdge(tree1, 2, 0);
- addUndirectedEdge(tree1, 3, 4);
- addUndirectedEdge(tree1, 2, 1);
- addUndirectedEdge(tree1, 2, 3);
-
- addUndirectedEdge(tree2, 1, 0);
- addUndirectedEdge(tree2, 2, 4);
- addUndirectedEdge(tree2, 1, 3);
- addUndirectedEdge(tree2, 1, 2);
-
- String encoding1 = encodeTree(tree1);
- String encoding2 = encodeTree(tree2);
-
- System.out.println("Tree1 encoding: " + encoding1);
- System.out.println("Tree2 encoding: " + encoding1);
- System.out.println("Trees are isomorphic: " + (encoding1.equals(encoding2)));
-
- // Print:
- // Tree1 encoding: (()())(())
- // Tree2 encoding: (()())(())
- // Trees are isomorphic: true
- }
-}
diff --git a/src/test/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyListTest.java b/src/test/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyListTest.java
index 4768393ae..0f1af416a 100644
--- a/src/test/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyListTest.java
+++ b/src/test/java/com/williamfiset/algorithms/graphtheory/networkflow/BipartiteGraphCheckAdjacencyListTest.java
@@ -1,80 +1,63 @@
package com.williamfiset.algorithms.graphtheory.networkflow;
import static com.google.common.truth.Truth.assertThat;
+import static com.williamfiset.algorithms.graphtheory.networkflow.BipartiteGraphCheckAdjacencyList.addUndirectedEdge;
+import static com.williamfiset.algorithms.graphtheory.networkflow.BipartiteGraphCheckAdjacencyList.createGraph;
-import com.williamfiset.algorithms.utils.graphutils.Utils;
-import java.util.*;
-import org.junit.jupiter.api.*;
+import java.util.List;
+import org.junit.jupiter.api.Test;
public class BipartiteGraphCheckAdjacencyListTest {
- private List
> graph;
- private BipartiteGraphCheckAdjacencyList solver;
-
- @BeforeEach
- public void setUp() {}
-
@Test
- public void testSingleton() {
- int n = 1;
- graph = Utils.createEmptyAdjacencyList(n);
- solver = new BipartiteGraphCheckAdjacencyList(graph);
- Utils.addUndirectedEdge(graph, 0, 0);
- assertThat(solver.isBipartite()).isFalse();
+ public void testSelfLoop() {
+ List
> g = createGraph(1);
+ addUndirectedEdge(g, 0, 0);
+ assertThat(new BipartiteGraphCheckAdjacencyList(g).isBipartite()).isFalse();
}
@Test
public void testTwoNodeGraph() {
- int n = 2;
- graph = Utils.createEmptyAdjacencyList(n);
- solver = new BipartiteGraphCheckAdjacencyList(graph);
- Utils.addUndirectedEdge(graph, 0, 1);
- assertThat(solver.isBipartite()).isTrue();
+ List
> g = createGraph(2);
+ addUndirectedEdge(g, 0, 1);
+ assertThat(new BipartiteGraphCheckAdjacencyList(g).isBipartite()).isTrue();
}
@Test
public void testTriangleGraph() {
- int n = 3;
- graph = Utils.createEmptyAdjacencyList(n);
- solver = new BipartiteGraphCheckAdjacencyList(graph);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 1, 2);
- Utils.addUndirectedEdge(graph, 2, 0);
- assertThat(solver.isBipartite()).isFalse();
+ List
> g = createGraph(3);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 1, 2);
+ addUndirectedEdge(g, 2, 0);
+ assertThat(new BipartiteGraphCheckAdjacencyList(g).isBipartite()).isFalse();
}
@Test
- public void testDisjointBipartiteGraphComponents() {
- int n = 4;
- graph = Utils.createEmptyAdjacencyList(n);
- solver = new BipartiteGraphCheckAdjacencyList(graph);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 2, 3);
- assertThat(solver.isBipartite()).isFalse();
+ public void testDisjointComponents() {
+ List
> g = createGraph(4);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 2, 3);
+ assertThat(new BipartiteGraphCheckAdjacencyList(g).isBipartite()).isFalse();
}
@Test
public void testSquareBipartiteGraph() {
- int n = 4;
- graph = Utils.createEmptyAdjacencyList(n);
- solver = new BipartiteGraphCheckAdjacencyList(graph);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 1, 2);
- Utils.addUndirectedEdge(graph, 2, 3);
- Utils.addUndirectedEdge(graph, 3, 0);
- assertThat(solver.isBipartite()).isTrue();
+ List
> g = createGraph(4);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 1, 2);
+ addUndirectedEdge(g, 2, 3);
+ addUndirectedEdge(g, 3, 0);
+ assertThat(new BipartiteGraphCheckAdjacencyList(g).isBipartite()).isTrue();
}
@Test
- public void testSquareBipartiteGraphWithAdditionalEdge() {
- int n = 4;
- graph = Utils.createEmptyAdjacencyList(n);
- solver = new BipartiteGraphCheckAdjacencyList(graph);
- Utils.addUndirectedEdge(graph, 0, 1);
- Utils.addUndirectedEdge(graph, 1, 2);
- Utils.addUndirectedEdge(graph, 0, 2);
- Utils.addUndirectedEdge(graph, 2, 3);
- Utils.addUndirectedEdge(graph, 3, 0);
- assertThat(solver.isBipartite()).isFalse();
+ public void testSquareWithDiagonal() {
+ List
> g = createGraph(4);
+ addUndirectedEdge(g, 0, 1);
+ addUndirectedEdge(g, 1, 2);
+ addUndirectedEdge(g, 0, 2);
+ addUndirectedEdge(g, 2, 3);
+ addUndirectedEdge(g, 3, 0);
+ assertThat(new BipartiteGraphCheckAdjacencyList(g).isBipartite()).isFalse();
}
}
diff --git a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/BUILD b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/BUILD
index e33fc8e4e..76461ee58 100644
--- a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/BUILD
+++ b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/BUILD
@@ -34,17 +34,6 @@ java_test(
deps = TEST_DEPS,
)
-# bazel test //src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms:LowestCommonAncestorTest
-java_test(
- name = "LowestCommonAncestorTest",
- srcs = ["LowestCommonAncestorTest.java"],
- main_class = "org.junit.platform.console.ConsoleLauncher",
- use_testrunner = False,
- args = ["--select-class=com.williamfiset.algorithms.graphtheory.treealgorithms.LowestCommonAncestorTest"],
- runtime_deps = JUNIT5_RUNTIME_DEPS,
- deps = TEST_DEPS,
-)
-
# bazel test //src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms:RootingTreeTest
java_test(
name = "RootingTreeTest",
@@ -89,16 +78,5 @@ java_test(
deps = TEST_DEPS,
)
-# bazel test //src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms:TreeIsomorphismWithBfsTest
-java_test(
- name = "TreeIsomorphismWithBfsTest",
- srcs = ["TreeIsomorphismWithBfsTest.java"],
- main_class = "org.junit.platform.console.ConsoleLauncher",
- use_testrunner = False,
- args = ["--select-class=com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphismWithBfsTest"],
- runtime_deps = JUNIT5_RUNTIME_DEPS,
- deps = TEST_DEPS,
-)
-
# Run all tests
# bazel test //src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms:all
diff --git a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/LowestCommonAncestorEulerTourTest.java b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/LowestCommonAncestorEulerTourTest.java
index 879de326f..f6e038157 100644
--- a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/LowestCommonAncestorEulerTourTest.java
+++ b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/LowestCommonAncestorEulerTourTest.java
@@ -181,34 +181,6 @@ public void testMalformedGraphThrows() {
() -> LowestCommonAncestorEulerTour.TreeNode.rootTree(tree, 0));
}
- @Test
- public void randomizedLcaQueriesVsOtherImpl() {
- for (int n = 1; n < 1000; n++) {
- List
> g = generateRandomTree(n);
-
- LowestCommonAncestor.TreeNode root1 = LowestCommonAncestor.TreeNode.rootTree(g, 0);
- LowestCommonAncestorEulerTour.TreeNode root2 =
- LowestCommonAncestorEulerTour.TreeNode.rootTree(g, 0);
-
- LowestCommonAncestor slowSolver = new LowestCommonAncestor(root1);
- LowestCommonAncestorEulerTour fastSolver = new LowestCommonAncestorEulerTour(root2);
-
- for (int i = 0; i < 100; i++) {
- int l = (int) (Math.random() * n);
- int r = (int) (Math.random() * n);
- int L = Math.min(l, r);
- int R = Math.max(l, r);
-
- LowestCommonAncestor.TreeNode lca1 = slowSolver.lca(L, R);
- LowestCommonAncestorEulerTour.TreeNode lca2 = fastSolver.lca(L, R);
-
- assertThat(lca1).isNotNull();
- assertThat(lca2).isNotNull();
- assertThat(lca1.id()).isEqualTo(lca2.index());
- }
- }
- }
-
public static List
> generateRandomTree(int n) {
List
> tree = createEmptyGraph(n);
-
- addUndirectedEdge(tree, 0, 1);
- addUndirectedEdge(tree, 0, 2);
- addUndirectedEdge(tree, 1, 3);
- addUndirectedEdge(tree, 1, 4);
- addUndirectedEdge(tree, 2, 5);
- addUndirectedEdge(tree, 2, 6);
- addUndirectedEdge(tree, 2, 7);
- addUndirectedEdge(tree, 3, 8);
- addUndirectedEdge(tree, 3, 9);
- addUndirectedEdge(tree, 5, 10);
- addUndirectedEdge(tree, 5, 11);
- addUndirectedEdge(tree, 7, 12);
- addUndirectedEdge(tree, 7, 13);
- addUndirectedEdge(tree, 11, 14);
- addUndirectedEdge(tree, 11, 15);
- addUndirectedEdge(tree, 11, 16);
-
- return LowestCommonAncestor.TreeNode.rootTree(tree, 0);
- }
-
- @Test
- public void testLcaTreeFromSlides1() {
- TreeNode root = createFirstTreeFromSlides();
- LowestCommonAncestor solver = new LowestCommonAncestor(root);
- assertThat(solver.lca(14, 13).id()).isEqualTo(2);
- assertThat(solver.lca(10, 16).id()).isEqualTo(5);
- assertThat(solver.lca(9, 11).id()).isEqualTo(0);
- }
-
- @Test
- public void testLcaTreeFromSlides2() {
- TreeNode root = createFirstTreeFromSlides();
- LowestCommonAncestor solver = new LowestCommonAncestor(root);
- assertThat(solver.lca(8, 9).id()).isEqualTo(3);
- assertThat(solver.lca(4, 8).id()).isEqualTo(1);
- assertThat(solver.lca(6, 13).id()).isEqualTo(2);
- assertThat(solver.lca(7, 13).id()).isEqualTo(7);
- assertThat(solver.lca(10, 5).id()).isEqualTo(5);
- assertThat(solver.lca(2, 16).id()).isEqualTo(2);
- }
-
- @Test
- public void testLcaOfTheSameNodeIsItself() {
- TreeNode root = createFirstTreeFromSlides();
- LowestCommonAncestor solver = new LowestCommonAncestor(root);
- // Try all nodes
- for (int id = 0; id < root.size(); id++) {
- assertThat(solver.lca(id, id).id()).isEqualTo(id);
- }
- }
-}
diff --git a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenterTest.java b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenterTest.java
index 783c3fc17..21b92fd5d 100644
--- a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenterTest.java
+++ b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeCenterTest.java
@@ -1,7 +1,3 @@
-// To run this test in isolation from root folder:
-//
-// $ bazel test //src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms:TreeCenterTest
-
package com.williamfiset.algorithms.graphtheory.treealgorithms;
import static com.google.common.truth.Truth.assertThat;
@@ -9,8 +5,9 @@
import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeCenter.createEmptyTree;
import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeCenter.findTreeCenters;
-import java.util.*;
-import org.junit.jupiter.api.*;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
public class TreeCenterTest {
@@ -69,6 +66,36 @@ public void simpleTest4() {
assertThat(findTreeCenters(graph)).containsExactly(2, 3);
}
+ @Test
+ public void starGraph() {
+ // 1
+ // |
+ // 2--0--3
+ // |
+ // 4
+ List
> graph = createEmptyTree(5);
+ addUndirectedEdge(graph, 0, 1);
+ addUndirectedEdge(graph, 0, 2);
+ addUndirectedEdge(graph, 0, 3);
+ addUndirectedEdge(graph, 0, 4);
+ assertThat(findTreeCenters(graph)).containsExactly(0);
+ }
+
+ @Test
+ public void asymmetricTree() {
+ // 0 - 1 - 2 - 3 - 4 - 5
+ // |
+ // 6
+ List
> graph = createEmptyTree(7);
+ addUndirectedEdge(graph, 0, 1);
+ addUndirectedEdge(graph, 1, 2);
+ addUndirectedEdge(graph, 2, 3);
+ addUndirectedEdge(graph, 3, 4);
+ addUndirectedEdge(graph, 4, 5);
+ addUndirectedEdge(graph, 4, 6);
+ assertThat(findTreeCenters(graph)).containsExactly(2, 3);
+ }
+
@Test
public void testTreeCenterVsOtherImpl() {
for (int n = 1; n < 500; n++) {
diff --git a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismTest.java b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismTest.java
index c5f840141..31e75eecb 100644
--- a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismTest.java
+++ b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismTest.java
@@ -127,27 +127,6 @@ public void differentNumberOfNodes() {
assertThat(treesAreIsomorphic(tree1, tree2)).isEqualTo(false);
}
- @Test
- public void testIsomorphismEquivilanceAgainstOtherImpl() {
- for (int n = 1; n < 50; n++) {
- for (int loops = 0; loops < 1000; loops++) {
- List
> tree1 = generateRandomTree(n);
- List
> tree2 = generateRandomTree(n);
-
- boolean impl1 = treesAreIsomorphic(tree1, tree2);
- boolean impl2 =
- com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphismWithBfs
- .treesAreIsomorphic(tree1, tree2);
- if (impl1 != impl2) {
- System.err.println("TreeIsomorphism algorithms disagree!");
- System.err.println(tree1);
- System.err.println(tree2);
- }
- assertThat(impl1).isEqualTo(impl2);
- }
- }
- }
-
// ==================== Encoding tests ====================
@Test
diff --git a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismWithBfsTest.java b/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismWithBfsTest.java
deleted file mode 100644
index bf1629816..000000000
--- a/src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms/TreeIsomorphismWithBfsTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-// To run this test in isolation from root folder:
-//
-// $ bazel test //src/test/java/com/williamfiset/algorithms/graphtheory/treealgorithms:TreeIsomorphismWithBfsTest
-
-package com.williamfiset.algorithms.graphtheory.treealgorithms;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphism.TreeNode;
-import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphismWithBfs.addUndirectedEdge;
-import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphismWithBfs.createEmptyTree;
-import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphismWithBfs.encodeTree;
-import static com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphismWithBfs.treesAreIsomorphic;
-
-import java.util.*;
-import org.junit.jupiter.api.*;
-
-public class TreeIsomorphismWithBfsTest {
-
- @Test
- public void testSingleton() {
- List
> tree1 = createEmptyTree(1);
- List
> tree2 = createEmptyTree(1);
- assertThat(treesAreIsomorphic(tree1, tree2)).isEqualTo(true);
- }
-
- @Test
- public void testTwoNodeTree() {
- List
> tree1 = createEmptyTree(2);
- List
> tree2 = createEmptyTree(2);
-
- addUndirectedEdge(tree1, 0, 1);
- addUndirectedEdge(tree2, 1, 0);
-
- assertThat(treesAreIsomorphic(tree1, tree2)).isEqualTo(true);
- }
-
- @Test
- public void testSmall() {
- List
> tree1 = createEmptyTree(5);
- List
> tree2 = createEmptyTree(5);
-
- addUndirectedEdge(tree1, 2, 0);
- addUndirectedEdge(tree1, 2, 1);
- addUndirectedEdge(tree1, 2, 3);
- addUndirectedEdge(tree1, 3, 4);
-
- addUndirectedEdge(tree2, 1, 3);
- addUndirectedEdge(tree2, 1, 0);
- addUndirectedEdge(tree2, 1, 2);
- addUndirectedEdge(tree2, 2, 4);
-
- assertThat(treesAreIsomorphic(tree1, tree2)).isEqualTo(true);
- }
-
- @Test
- public void testSimilarChains() {
- // Trees 1 and 3 are equal
- int n = 10;
- List
> tree1 = createEmptyTree(n);
- List
> tree2 = createEmptyTree(n);
- List
> tree3 = createEmptyTree(n);
-
- addUndirectedEdge(tree1, 0, 1);
- addUndirectedEdge(tree1, 1, 3);
- addUndirectedEdge(tree1, 3, 5);
- addUndirectedEdge(tree1, 5, 7);
- addUndirectedEdge(tree1, 7, 8);
- addUndirectedEdge(tree1, 8, 9);
- addUndirectedEdge(tree1, 2, 1);
- addUndirectedEdge(tree1, 4, 3);
- addUndirectedEdge(tree1, 6, 5);
-
- addUndirectedEdge(tree2, 0, 1);
- addUndirectedEdge(tree2, 1, 3);
- addUndirectedEdge(tree2, 3, 5);
- addUndirectedEdge(tree2, 5, 6);
- addUndirectedEdge(tree2, 6, 8);
- addUndirectedEdge(tree2, 8, 9);
- addUndirectedEdge(tree2, 6, 7);
- addUndirectedEdge(tree2, 4, 3);
- addUndirectedEdge(tree2, 2, 1);
-
- addUndirectedEdge(tree3, 0, 1);
- addUndirectedEdge(tree3, 1, 8);
- addUndirectedEdge(tree3, 1, 6);
- addUndirectedEdge(tree3, 6, 4);
- addUndirectedEdge(tree3, 6, 5);
- addUndirectedEdge(tree3, 5, 3);
- addUndirectedEdge(tree3, 5, 7);
- addUndirectedEdge(tree3, 7, 2);
- addUndirectedEdge(tree3, 2, 9);
-
- assertThat(treesAreIsomorphic(tree1, tree2)).isEqualTo(false);
- assertThat(treesAreIsomorphic(tree1, tree3)).isEqualTo(true);
- assertThat(treesAreIsomorphic(tree2, tree3)).isEqualTo(false);
- }
-
- @Test
- public void testSlidesExample() {
- // Setup tree structure from:
- // http://webhome.cs.uvic.ca/~wendym/courses/582/16/notes/582_12_tree_can_form.pdf
- List
> tree = createEmptyTree(19);
-
- addUndirectedEdge(tree, 6, 2);
- addUndirectedEdge(tree, 6, 7);
- addUndirectedEdge(tree, 6, 11);
- addUndirectedEdge(tree, 7, 8);
- addUndirectedEdge(tree, 7, 9);
- addUndirectedEdge(tree, 7, 10);
- addUndirectedEdge(tree, 11, 12);
- addUndirectedEdge(tree, 11, 13);
- addUndirectedEdge(tree, 11, 16);
- addUndirectedEdge(tree, 13, 14);
- addUndirectedEdge(tree, 13, 15);
- addUndirectedEdge(tree, 16, 17);
- addUndirectedEdge(tree, 16, 18);
- addUndirectedEdge(tree, 2, 0);
- addUndirectedEdge(tree, 2, 1);
- addUndirectedEdge(tree, 2, 3);
- addUndirectedEdge(tree, 2, 4);
- addUndirectedEdge(tree, 4, 5);
-
- String treeEncoding = encodeTree(tree);
- String expectedEncoding = "(((()())(()())())((())()()())(()()()))";
- assertThat(treeEncoding).isEqualTo(expectedEncoding);
- }
-
- @Test
- public void t() {
- List
> tree = createEmptyTree(10);
-
- TreeNode node0 = new TreeNode(0);
- TreeNode node1 = new TreeNode(1);
- TreeNode node2 = new TreeNode(2);
- TreeNode node3 = new TreeNode(3);
- TreeNode node4 = new TreeNode(4);
- TreeNode node5 = new TreeNode(5);
- TreeNode node6 = new TreeNode(6);
- TreeNode node7 = new TreeNode(7);
- TreeNode node8 = new TreeNode(8);
- TreeNode node9 = new TreeNode(9);
-
- node0.addChildren(node1, node2, node3);
- node1.addChildren(node4, node5);
- node5.addChildren(node9);
- node2.addChildren(node6, node7);
- node3.addChildren(node8);
-
- // TODO(william): finish this test to check for "(((())())(()())(()))" encoding
- // System.out.println(
- // com.williamfiset.algorithms.graphtheory.treealgorithms.TreeIsomorphism.encode(node0));
-
- // (((())())(()())(()))
- // ((())())
- // (()())
- // (())
- //
-
- // (()())
- // (())
- // (())
-
- // ((()())(()))
- // ((())())
- //
- // ((()())(()))((())())
-
- // (((()())(()))((())()))
- // (()())
- // (())
- //
- // ((())())
- //
- }
-}