From d3b0e3bf1a4bb27db36fd8773c5d5e3386866738 Mon Sep 17 00:00:00 2001
From: sharan
Date: Fri, 10 Oct 2025 23:04:30 +0530
Subject: [PATCH 1/7] feat: Add Hierholzer's Algorithm for Eulerian Circuits
---
.../graph/HierholzerAlgorithm.java | 125 ++++++++++++++++++
.../graph/HierholzerAlgorithmTest.java | 46 +++++++
2 files changed, 171 insertions(+)
create mode 100644 src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
create mode 100644 src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
diff --git a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
new file mode 100644
index 000000000000..8f36e3377d6e
--- /dev/null
+++ b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
@@ -0,0 +1,125 @@
+package com.thealgorithms.graph;
+
+import java.util.*;
+
+/**
+ * An implementation of Hierholzer's Algorithm to find an Eulerian Path or Circuit in an undirected graph.
+ * This algorithm finds a trail in a graph that visits every edge exactly once.
+ * Think of it like solving a puzzle where you trace every line without lifting your pen.
+ *
+ * Wikipedia: https://en.wikipedia.org/wiki/Eulerian_path#Hierholzer's_algorithm
+ */
+public class HierholzerAlgorithm {
+
+ private final Map> graph;
+
+ /**
+ * Sets up the algorithm with the graph we want to solve.
+ * @param graph The graph represented as an adjacency list.
+ */
+ public HierholzerAlgorithm(Map> graph) {
+ this.graph = (graph == null) ? new HashMap<>() : graph;
+ }
+
+ /**
+ * Before starting, we have to ask: can this puzzle even be solved?
+ * This method checks the two essential rules for an undirected graph.
+ * @return true if a circuit is possible, false otherwise.
+ */
+ public boolean hasEulerianCircuit() {
+ if (graph.isEmpty()) {
+ return true; // An empty puzzle is trivially solved.
+ }
+
+ // Rule 1: Every point must have an even number of lines connected to it.
+ // This ensures for every way in, there's a way out.
+ for (int vertex : graph.keySet()) {
+ if (graph.get(vertex).size() % 2 != 0) {
+ return false; // Found a point with an odd number of lines.
+ }
+ }
+
+ // Rule 2: The drawing must be one single, connected piece.
+ // You can't have a separate, floating part of the puzzle.
+ return isCoherentlyConnected();
+ }
+
+ /**
+ * This is the main event—finding the actual path.
+ * @return A list of points (vertices) that make up the complete circuit.
+ */
+ public List findEulerianCircuit() {
+ if (!hasEulerianCircuit()) {
+ // If the puzzle can't be solved, return an empty path.
+ return Collections.emptyList();
+ }
+
+ // We'll work on a copy of the graph so we don't destroy the original.
+ Map> tempGraph = new HashMap<>();
+ for (Map.Entry> entry : graph.entrySet()) {
+ tempGraph.put(entry.getKey(), new LinkedList<>(entry.getValue()));
+ }
+
+ // 'currentPath' is our breadcrumb trail as we explore.
+ Stack currentPath = new Stack<>();
+ // 'circuit' is where we'll lay out the final, complete path.
+ List circuit = new LinkedList<>();
+
+ // Find any point to start from.
+ int startVertex = graph.keySet().stream().findFirst().orElse(-1);
+ if (startVertex == -1) return Collections.emptyList();
+
+ currentPath.push(startVertex);
+
+ while (!currentPath.isEmpty()) {
+ int currentVertex = currentPath.peek();
+
+ // If there's an unexplored hallway from our current location...
+ if (tempGraph.containsKey(currentVertex) && !tempGraph.get(currentVertex).isEmpty()) {
+ // ...let's go down it.
+ int nextVertex = tempGraph.get(currentVertex).pollFirst();
+ // Erase the hallway behind us so we don't use it again.
+ tempGraph.get(nextVertex).remove(Integer.valueOf(currentVertex));
+ // Add the new location to our breadcrumb trail.
+ currentPath.push(nextVertex);
+ } else {
+ // If we've hit a dead end, we're done with this part of the tour.
+ // We add our location to the final path and backtrack.
+ circuit.add(0, currentPath.pop());
+ }
+ }
+
+ return circuit;
+ }
+
+ /**
+ * A helper to check if the graph is one single piece.
+ * It does a simple walk (DFS) starting from one point and checks if it can reach all other points.
+ */
+ private boolean isCoherentlyConnected() {
+ if (graph.isEmpty()) return true;
+ Set visited = new HashSet<>();
+ int startNode = graph.keySet().stream().findFirst().orElse(-1);
+ if (startNode == -1) return true;
+
+ dfs(startNode, visited);
+
+ for (int vertex : graph.keySet()) {
+ if (!graph.get(vertex).isEmpty() && !visited.contains(vertex)) {
+ return false; // Found a part of the puzzle we couldn't reach.
+ }
+ }
+ return true;
+ }
+
+ private void dfs(int u, Set visited) {
+ visited.add(u);
+ if (graph.containsKey(u)) {
+ for (int v : graph.get(u)) {
+ if (!visited.contains(v)) {
+ dfs(v, visited);
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
new file mode 100644
index 000000000000..ee479141994b
--- /dev/null
+++ b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
@@ -0,0 +1,46 @@
+package com.thealgorithms.graph;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.*;
+import org.junit.jupiter.api.Test;
+
+public class HierholzerAlgorithmTest {
+
+ @Test
+ public void testFindsEulerianCircuitInSimpleTriangleGraph() {
+ // Create a simple triangle graph where a circuit definitely exists: 0-1, 1-2, 2-0
+ Map> graph = new HashMap<>();
+ graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
+ graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
+ graph.put(2, new LinkedList<>(Arrays.asList(0, 1)));
+
+ HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
+
+ // Verify that the algorithm agrees a circuit exists
+ assertTrue(algorithm.hasEulerianCircuit());
+
+ List circuit = algorithm.findEulerianCircuit();
+
+ // A valid circuit for a triangle has 3 edges, so the path will have 4 vertices (e.g., 0 -> 1 -> 2 -> 0)
+ assertEquals(4, circuit.size());
+
+ // The path must start and end at the same vertex
+ assertEquals(circuit.get(0), circuit.get(circuit.size() - 1));
+ }
+
+ @Test
+ public void testHandlesGraphWithNoEulerianCircuit() {
+ // Create a graph where a vertex has an odd degree
+ Map> graph = new HashMap<>();
+ graph.put(0, new LinkedList<>(Collections.singletonList(1)));
+ graph.put(1, new LinkedList<>(Collections.singletonList(0)));
+ graph.put(2, new LinkedList<>(Collections.emptyList())); // Vertex 2 is isolated
+
+ HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
+
+ // The algorithm should correctly identify that no circuit exists
+ assertEquals(false, algorithm.hasEulerianCircuit());
+ }
+}
From 568913c6a62308d8f3b055c9b7629f590a9c3b96 Mon Sep 17 00:00:00 2001
From: sharan
Date: Sat, 11 Oct 2025 11:54:34 +0530
Subject: [PATCH 2/7] fix: Add more test cases to improve code coverage
---
.../graph/HierholzerAlgorithm.java | 119 ++++++++++++------
.../graph/HierholzerAlgorithmTest.java | 42 ++++++-
2 files changed, 119 insertions(+), 42 deletions(-)
diff --git a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
index 8f36e3377d6e..d1a849c36b57 100644
--- a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
+++ b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
@@ -1,90 +1,118 @@
package com.thealgorithms.graph;
-import java.util.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
/**
* An implementation of Hierholzer's Algorithm to find an Eulerian Path or Circuit in an undirected graph.
- * This algorithm finds a trail in a graph that visits every edge exactly once.
- * Think of it like solving a puzzle where you trace every line without lifting your pen.
+ * An Eulerian path is a trail in a graph that visits every edge exactly once.
+ * An Eulerian circuit is an Eulerian path that starts and ends at the same vertex.
*
* Wikipedia: https://en.wikipedia.org/wiki/Eulerian_path#Hierholzer's_algorithm
*/
public class HierholzerAlgorithm {
+ private final int numVertices;
private final Map> graph;
/**
- * Sets up the algorithm with the graph we want to solve.
+ * Constructor for the algorithm.
* @param graph The graph represented as an adjacency list.
+ * Using a LinkedList for neighbors is efficient for edge removal.
*/
public HierholzerAlgorithm(Map> graph) {
- this.graph = (graph == null) ? new HashMap<>() : graph;
+ if (graph == null) {
+ this.graph = new HashMap<>();
+ this.numVertices = 0;
+ return;
+ }
+ this.graph = graph;
+ this.numVertices = graph.size();
}
/**
- * Before starting, we have to ask: can this puzzle even be solved?
- * This method checks the two essential rules for an undirected graph.
- * @return true if a circuit is possible, false otherwise.
+ * Checks if an Eulerian circuit exists in the undirected graph.
+ * Condition: All vertices with a non-zero degree must be in a single connected component,
+ * and all vertices must have an even degree.
+ * @return true if a circuit exists, false otherwise.
*/
public boolean hasEulerianCircuit() {
if (graph.isEmpty()) {
- return true; // An empty puzzle is trivially solved.
+ return true; // An empty graph has an empty circuit.
}
- // Rule 1: Every point must have an even number of lines connected to it.
- // This ensures for every way in, there's a way out.
+ // Check 1: All vertices must have an even degree.
for (int vertex : graph.keySet()) {
if (graph.get(vertex).size() % 2 != 0) {
- return false; // Found a point with an odd number of lines.
+ return false; // Found a vertex with an odd degree.
}
}
- // Rule 2: The drawing must be one single, connected piece.
- // You can't have a separate, floating part of the puzzle.
- return isCoherentlyConnected();
+ // Check 2: All vertices with edges must be connected.
+ if (!isCoherentlyConnected()) {
+ return false;
+ }
+
+ return true;
}
/**
- * This is the main event—finding the actual path.
- * @return A list of points (vertices) that make up the complete circuit.
+ * Finds the Eulerian circuit.
+ * @return A list of vertices representing the circuit, or an empty list if none exists.
*/
public List findEulerianCircuit() {
if (!hasEulerianCircuit()) {
- // If the puzzle can't be solved, return an empty path.
return Collections.emptyList();
}
- // We'll work on a copy of the graph so we don't destroy the original.
+ // Create a copy of the graph to avoid modifying the original during traversal.
Map> tempGraph = new HashMap<>();
for (Map.Entry> entry : graph.entrySet()) {
tempGraph.put(entry.getKey(), new LinkedList<>(entry.getValue()));
}
- // 'currentPath' is our breadcrumb trail as we explore.
+ // Data structures for the algorithm.
Stack currentPath = new Stack<>();
- // 'circuit' is where we'll lay out the final, complete path.
List circuit = new LinkedList<>();
- // Find any point to start from.
- int startVertex = graph.keySet().stream().findFirst().orElse(-1);
- if (startVertex == -1) return Collections.emptyList();
+ // Find a starting vertex (any vertex with edges).
+ int startVertex = -1;
+ for (int vertex : tempGraph.keySet()) {
+ if (!tempGraph.get(vertex).isEmpty()) {
+ startVertex = vertex;
+ break;
+ }
+ }
+
+ if (startVertex == -1) {
+ if (graph.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(graph.keySet().iterator().next()); // Graph with one isolated vertex.
+ }
currentPath.push(startVertex);
while (!currentPath.isEmpty()) {
int currentVertex = currentPath.peek();
- // If there's an unexplored hallway from our current location...
+ // If the current vertex has unvisited edges
if (tempGraph.containsKey(currentVertex) && !tempGraph.get(currentVertex).isEmpty()) {
- // ...let's go down it.
- int nextVertex = tempGraph.get(currentVertex).pollFirst();
- // Erase the hallway behind us so we don't use it again.
+ int nextVertex = tempGraph.get(currentVertex).pollFirst(); // Get a neighbor
+
+ // Remove the reverse edge as well (for undirected graph)
tempGraph.get(nextVertex).remove(Integer.valueOf(currentVertex));
- // Add the new location to our breadcrumb trail.
+
+ // Push the neighbor to the stack to continue the tour
currentPath.push(nextVertex);
} else {
- // If we've hit a dead end, we're done with this part of the tour.
- // We add our location to the final path and backtrack.
+ // If "stuck" (no more edges), backtrack and add to the final circuit.
circuit.add(0, currentPath.pop());
}
}
@@ -93,20 +121,37 @@ public List findEulerianCircuit() {
}
/**
- * A helper to check if the graph is one single piece.
- * It does a simple walk (DFS) starting from one point and checks if it can reach all other points.
+ * Helper method to check if all vertices with a non-zero degree are connected.
+ * Uses a simple traversal (DFS).
*/
private boolean isCoherentlyConnected() {
- if (graph.isEmpty()) return true;
+ if (graph.isEmpty()) {
+ return true;
+ }
+
Set visited = new HashSet<>();
- int startNode = graph.keySet().stream().findFirst().orElse(-1);
- if (startNode == -1) return true;
+ int startNode = -1;
+
+ // Find the first vertex with a degree greater than 0
+ for (int vertex : graph.keySet()) {
+ if (!graph.get(vertex).isEmpty()) {
+ startNode = vertex;
+ break;
+ }
+ }
+
+ // If no edges in the graph, it's connected.
+ if (startNode == -1) {
+ return true;
+ }
+ // Perform DFS from the start node
dfs(startNode, visited);
+ // Check if all vertices with edges were visited
for (int vertex : graph.keySet()) {
if (!graph.get(vertex).isEmpty() && !visited.contains(vertex)) {
- return false; // Found a part of the puzzle we couldn't reach.
+ return false; // Found a vertex with edges that wasn't visited
}
}
return true;
diff --git a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
index ee479141994b..f2daf82a272d 100644
--- a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
+++ b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
@@ -1,9 +1,15 @@
package com.thealgorithms.graph;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import org.junit.jupiter.api.Test;
public class HierholzerAlgorithmTest {
@@ -31,16 +37,42 @@ public void testFindsEulerianCircuitInSimpleTriangleGraph() {
}
@Test
- public void testHandlesGraphWithNoEulerianCircuit() {
- // Create a graph where a vertex has an odd degree
+ public void testFailsForGraphWithOddDegreeVertices() {
+ // Create a graph where vertices 0 and 1 have an odd degree (1)
Map> graph = new HashMap<>();
graph.put(0, new LinkedList<>(Collections.singletonList(1)));
graph.put(1, new LinkedList<>(Collections.singletonList(0)));
- graph.put(2, new LinkedList<>(Collections.emptyList())); // Vertex 2 is isolated
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
// The algorithm should correctly identify that no circuit exists
- assertEquals(false, algorithm.hasEulerianCircuit());
+ assertFalse(algorithm.hasEulerianCircuit());
+ // The find method should return an empty list
+ assertTrue(algorithm.findEulerianCircuit().isEmpty());
+ }
+
+ @Test
+ public void testFailsForDisconnectedGraph() {
+ // Create a graph with two separate triangles (0-1-2 and 3-4-5)
+ Map> graph = new HashMap<>();
+ graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
+ graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
+ graph.put(2, new LinkedList<>(Arrays.asList(0, 1)));
+ graph.put(3, new LinkedList<>(Arrays.asList(4, 5)));
+ graph.put(4, new LinkedList<>(Arrays.asList(3, 5)));
+ graph.put(5, new LinkedList<>(Arrays.asList(3, 4)));
+
+ HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
+
+ // All degrees are even, but the graph is not connected, so no circuit exists
+ assertFalse(algorithm.hasEulerianCircuit());
+ }
+
+ @Test
+ public void testHandlesEmptyGraph() {
+ Map> graph = new HashMap<>();
+ HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
+ assertTrue(algorithm.hasEulerianCircuit());
+ assertTrue(algorithm.findEulerianCircuit().isEmpty());
}
}
From 456e0cfd6c57218c42103515860bbc62a746ec5b Mon Sep 17 00:00:00 2001
From: sharan
Date: Sat, 11 Oct 2025 12:18:56 +0530
Subject: [PATCH 3/7] feat: Add Hierholzer's Algorithm for Eulerian Circuits
---
.../graph/HierholzerAlgorithmTest.java | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
index f2daf82a272d..9c9f468c9bb4 100644
--- a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
+++ b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
@@ -1,22 +1,14 @@
package com.thealgorithms.graph;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import static org.junit.jupiter.api.Assertions.*;
+import java.util.*;
import org.junit.jupiter.api.Test;
public class HierholzerAlgorithmTest {
@Test
public void testFindsEulerianCircuitInSimpleTriangleGraph() {
- // Create a simple triangle graph where a circuit definitely exists: 0-1, 1-2, 2-0
+ // A simple triangle graph where a circuit definitely exists: 0-1, 1-2, 2-0
Map> graph = new HashMap<>();
graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
@@ -50,7 +42,7 @@ public void testFailsForGraphWithOddDegreeVertices() {
// The find method should return an empty list
assertTrue(algorithm.findEulerianCircuit().isEmpty());
}
-
+
@Test
public void testFailsForDisconnectedGraph() {
// Create a graph with two separate triangles (0-1-2 and 3-4-5)
From 96bd4fce9052d0a2959c43ad3f9f8bfbf5347b5e Mon Sep 17 00:00:00 2001
From: sharan
Date: Sat, 11 Oct 2025 12:20:37 +0530
Subject: [PATCH 4/7] fix: Apply clang-format after merge
---
.../graph/HierholzerAlgorithmTest.java | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
index 9c9f468c9bb4..f2daf82a272d 100644
--- a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
+++ b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
@@ -1,14 +1,22 @@
package com.thealgorithms.graph;
-import static org.junit.jupiter.api.Assertions.*;
-import java.util.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import org.junit.jupiter.api.Test;
public class HierholzerAlgorithmTest {
@Test
public void testFindsEulerianCircuitInSimpleTriangleGraph() {
- // A simple triangle graph where a circuit definitely exists: 0-1, 1-2, 2-0
+ // Create a simple triangle graph where a circuit definitely exists: 0-1, 1-2, 2-0
Map> graph = new HashMap<>();
graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
@@ -42,7 +50,7 @@ public void testFailsForGraphWithOddDegreeVertices() {
// The find method should return an empty list
assertTrue(algorithm.findEulerianCircuit().isEmpty());
}
-
+
@Test
public void testFailsForDisconnectedGraph() {
// Create a graph with two separate triangles (0-1-2 and 3-4-5)
From 0fac8064010d92316a5f3fd80029981f46e4f930 Mon Sep 17 00:00:00 2001
From: sharan
Date: Sat, 11 Oct 2025 12:37:59 +0530
Subject: [PATCH 5/7] fix: Apply all formatting, style, and efficiency fixes
---
.../graph/HierholzerAlgorithm.java | 84 +++++--------------
.../graph/HierholzerAlgorithmTest.java | 18 ----
2 files changed, 20 insertions(+), 82 deletions(-)
diff --git a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
index d1a849c36b57..e9f15cec1d82 100644
--- a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
+++ b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
@@ -9,52 +9,26 @@
import java.util.Set;
import java.util.Stack;
-/**
- * An implementation of Hierholzer's Algorithm to find an Eulerian Path or Circuit in an undirected graph.
- * An Eulerian path is a trail in a graph that visits every edge exactly once.
- * An Eulerian circuit is an Eulerian path that starts and ends at the same vertex.
- *
- * Wikipedia: https://en.wikipedia.org/wiki/Eulerian_path#Hierholzer's_algorithm
- */
public class HierholzerAlgorithm {
- private final int numVertices;
private final Map> graph;
- /**
- * Constructor for the algorithm.
- * @param graph The graph represented as an adjacency list.
- * Using a LinkedList for neighbors is efficient for edge removal.
- */
public HierholzerAlgorithm(Map> graph) {
- if (graph == null) {
- this.graph = new HashMap<>();
- this.numVertices = 0;
- return;
- }
- this.graph = graph;
- this.numVertices = graph.size();
+ this.graph = (graph == null) ? new HashMap<>() : graph;
}
- /**
- * Checks if an Eulerian circuit exists in the undirected graph.
- * Condition: All vertices with a non-zero degree must be in a single connected component,
- * and all vertices must have an even degree.
- * @return true if a circuit exists, false otherwise.
- */
public boolean hasEulerianCircuit() {
if (graph.isEmpty()) {
- return true; // An empty graph has an empty circuit.
+ return true;
}
- // Check 1: All vertices must have an even degree.
- for (int vertex : graph.keySet()) {
- if (graph.get(vertex).size() % 2 != 0) {
- return false; // Found a vertex with an odd degree.
+ // FINAL FIX: Loop over values directly for efficiency.
+ for (List neighbors : graph.values()) {
+ if (neighbors.size() % 2 != 0) {
+ return false;
}
}
- // Check 2: All vertices with edges must be connected.
if (!isCoherentlyConnected()) {
return false;
}
@@ -62,30 +36,24 @@ public boolean hasEulerianCircuit() {
return true;
}
- /**
- * Finds the Eulerian circuit.
- * @return A list of vertices representing the circuit, or an empty list if none exists.
- */
public List findEulerianCircuit() {
if (!hasEulerianCircuit()) {
return Collections.emptyList();
}
- // Create a copy of the graph to avoid modifying the original during traversal.
Map> tempGraph = new HashMap<>();
for (Map.Entry> entry : graph.entrySet()) {
tempGraph.put(entry.getKey(), new LinkedList<>(entry.getValue()));
}
- // Data structures for the algorithm.
Stack currentPath = new Stack<>();
List circuit = new LinkedList<>();
- // Find a starting vertex (any vertex with edges).
int startVertex = -1;
- for (int vertex : tempGraph.keySet()) {
- if (!tempGraph.get(vertex).isEmpty()) {
- startVertex = vertex;
+ // FINAL FIX: Use entrySet for efficiency.
+ for (Map.Entry> entry : tempGraph.entrySet()) {
+ if (!entry.getValue().isEmpty()) {
+ startVertex = entry.getKey();
break;
}
}
@@ -94,7 +62,7 @@ public List findEulerianCircuit() {
if (graph.isEmpty()) {
return Collections.emptyList();
}
- return Collections.singletonList(graph.keySet().iterator().next()); // Graph with one isolated vertex.
+ return Collections.singletonList(graph.keySet().iterator().next());
}
currentPath.push(startVertex);
@@ -102,17 +70,11 @@ public List findEulerianCircuit() {
while (!currentPath.isEmpty()) {
int currentVertex = currentPath.peek();
- // If the current vertex has unvisited edges
if (tempGraph.containsKey(currentVertex) && !tempGraph.get(currentVertex).isEmpty()) {
- int nextVertex = tempGraph.get(currentVertex).pollFirst(); // Get a neighbor
-
- // Remove the reverse edge as well (for undirected graph)
+ int nextVertex = tempGraph.get(currentVertex).pollFirst();
tempGraph.get(nextVertex).remove(Integer.valueOf(currentVertex));
-
- // Push the neighbor to the stack to continue the tour
currentPath.push(nextVertex);
} else {
- // If "stuck" (no more edges), backtrack and add to the final circuit.
circuit.add(0, currentPath.pop());
}
}
@@ -120,10 +82,6 @@ public List findEulerianCircuit() {
return circuit;
}
- /**
- * Helper method to check if all vertices with a non-zero degree are connected.
- * Uses a simple traversal (DFS).
- */
private boolean isCoherentlyConnected() {
if (graph.isEmpty()) {
return true;
@@ -132,26 +90,24 @@ private boolean isCoherentlyConnected() {
Set visited = new HashSet<>();
int startNode = -1;
- // Find the first vertex with a degree greater than 0
- for (int vertex : graph.keySet()) {
- if (!graph.get(vertex).isEmpty()) {
- startNode = vertex;
+ // FINAL FIX: Use entrySet for efficiency.
+ for (Map.Entry> entry : graph.entrySet()) {
+ if (!entry.getValue().isEmpty()) {
+ startNode = entry.getKey();
break;
}
}
- // If no edges in the graph, it's connected.
if (startNode == -1) {
return true;
}
- // Perform DFS from the start node
dfs(startNode, visited);
- // Check if all vertices with edges were visited
- for (int vertex : graph.keySet()) {
- if (!graph.get(vertex).isEmpty() && !visited.contains(vertex)) {
- return false; // Found a vertex with edges that wasn't visited
+ // FINAL FIX: Use entrySet for efficiency.
+ for (Map.Entry> entry : graph.entrySet()) {
+ if (!entry.getValue().isEmpty() && !visited.contains(entry.getKey())) {
+ return false;
}
}
return true;
diff --git a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
index f2daf82a272d..4dadb206d134 100644
--- a/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
+++ b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
@@ -16,44 +16,29 @@ public class HierholzerAlgorithmTest {
@Test
public void testFindsEulerianCircuitInSimpleTriangleGraph() {
- // Create a simple triangle graph where a circuit definitely exists: 0-1, 1-2, 2-0
Map> graph = new HashMap<>();
graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
graph.put(2, new LinkedList<>(Arrays.asList(0, 1)));
-
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
-
- // Verify that the algorithm agrees a circuit exists
assertTrue(algorithm.hasEulerianCircuit());
-
List circuit = algorithm.findEulerianCircuit();
-
- // A valid circuit for a triangle has 3 edges, so the path will have 4 vertices (e.g., 0 -> 1 -> 2 -> 0)
assertEquals(4, circuit.size());
-
- // The path must start and end at the same vertex
assertEquals(circuit.get(0), circuit.get(circuit.size() - 1));
}
@Test
public void testFailsForGraphWithOddDegreeVertices() {
- // Create a graph where vertices 0 and 1 have an odd degree (1)
Map> graph = new HashMap<>();
graph.put(0, new LinkedList<>(Collections.singletonList(1)));
graph.put(1, new LinkedList<>(Collections.singletonList(0)));
-
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
-
- // The algorithm should correctly identify that no circuit exists
assertFalse(algorithm.hasEulerianCircuit());
- // The find method should return an empty list
assertTrue(algorithm.findEulerianCircuit().isEmpty());
}
@Test
public void testFailsForDisconnectedGraph() {
- // Create a graph with two separate triangles (0-1-2 and 3-4-5)
Map> graph = new HashMap<>();
graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
@@ -61,10 +46,7 @@ public void testFailsForDisconnectedGraph() {
graph.put(3, new LinkedList<>(Arrays.asList(4, 5)));
graph.put(4, new LinkedList<>(Arrays.asList(3, 5)));
graph.put(5, new LinkedList<>(Arrays.asList(3, 4)));
-
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
-
- // All degrees are even, but the graph is not connected, so no circuit exists
assertFalse(algorithm.hasEulerianCircuit());
}
From 1de236eaff9dda0a9efe6e3d54118c6af4934936 Mon Sep 17 00:00:00 2001
From: sharan
Date: Mon, 13 Oct 2025 06:22:48 +0530
Subject: [PATCH 6/7] docs: Apply feedback and improve Javadoc
---
.../graph/HierholzerAlgorithm.java | 38 +++++++++++++------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
index e9f15cec1d82..a804f77d7fa6 100644
--- a/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
+++ b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
@@ -9,7 +9,28 @@
import java.util.Set;
import java.util.Stack;
-public class HierholzerAlgorithm {
+/**
+ * Implementation of Hierholzer's algorithm to find an Eulerian Circuit in an undirected graph.
+ *
+ * An Eulerian circuit is a trail in a graph that visits every edge exactly once,
+ * starting and ending at the same vertex. This algorithm finds such a circuit if one exists.
+ *
+ *
+ * This implementation is designed for an undirected graph. For a valid Eulerian
+ * circuit to exist, the graph must satisfy two conditions:
+ *
+ * - All vertices with a non-zero degree must be part of a single connected component.
+ * - Every vertex must have an even degree (an even number of edges connected to it).
+ *
+ *
+ *
+ * The algorithm runs in O(E + V) time, where E is the number of edges and V is the number of vertices.
+ * The graph is represented by a Map where keys are vertices and values are a LinkedList of adjacent vertices.
+ *
+ *
+ * @see Wikipedia: Hierholzer's algorithm
+ */
+public final class HierholzerAlgorithm {
private final Map> graph;
@@ -22,18 +43,13 @@ public boolean hasEulerianCircuit() {
return true;
}
- // FINAL FIX: Loop over values directly for efficiency.
for (List neighbors : graph.values()) {
if (neighbors.size() % 2 != 0) {
return false;
}
}
- if (!isCoherentlyConnected()) {
- return false;
- }
-
- return true;
+ return isCoherentlyConnected();
}
public List findEulerianCircuit() {
@@ -47,10 +63,9 @@ public List findEulerianCircuit() {
}
Stack currentPath = new Stack<>();
- List circuit = new LinkedList<>();
+ LinkedList circuit = new LinkedList<>();
int startVertex = -1;
- // FINAL FIX: Use entrySet for efficiency.
for (Map.Entry> entry : tempGraph.entrySet()) {
if (!entry.getValue().isEmpty()) {
startVertex = entry.getKey();
@@ -75,7 +90,8 @@ public List findEulerianCircuit() {
tempGraph.get(nextVertex).remove(Integer.valueOf(currentVertex));
currentPath.push(nextVertex);
} else {
- circuit.add(0, currentPath.pop());
+ circuit.addFirst(currentVertex);
+ currentPath.pop();
}
}
@@ -90,7 +106,6 @@ private boolean isCoherentlyConnected() {
Set visited = new HashSet<>();
int startNode = -1;
- // FINAL FIX: Use entrySet for efficiency.
for (Map.Entry> entry : graph.entrySet()) {
if (!entry.getValue().isEmpty()) {
startNode = entry.getKey();
@@ -104,7 +119,6 @@ private boolean isCoherentlyConnected() {
dfs(startNode, visited);
- // FINAL FIX: Use entrySet for efficiency.
for (Map.Entry> entry : graph.entrySet()) {
if (!entry.getValue().isEmpty() && !visited.contains(entry.getKey())) {
return false;
From ef458a2305be1a49e6b12da071d7b5d8ecb539f3 Mon Sep 17 00:00:00 2001
From: sharan
Date: Mon, 13 Oct 2025 06:38:25 +0530
Subject: [PATCH 7/7] docs: Add Hierholzer's Algorithm to DIRECTORY.md
---
DIRECTORY.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/DIRECTORY.md b/DIRECTORY.md
index b311b10fa177..053dbaac5b7e 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -164,6 +164,7 @@
- 📄 [FordFulkerson](src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java)
- 📄 [Graphs](src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java)
- 📄 [HamiltonianCycle](src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java)
+ - 📄 [HierholzerAlgorithm](src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java)
- 📄 [JohnsonsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java)
- 📄 [KahnsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java)
- 📄 [Kosaraju](src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java)