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)
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..a804f77d7fa6
--- /dev/null
+++ b/src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
@@ -0,0 +1,140 @@
+package com.thealgorithms.graph;
+
+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;
+
+/**
+ * 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;
+
+ public HierholzerAlgorithm(Map> graph) {
+ this.graph = (graph == null) ? new HashMap<>() : graph;
+ }
+
+ public boolean hasEulerianCircuit() {
+ if (graph.isEmpty()) {
+ return true;
+ }
+
+ for (List neighbors : graph.values()) {
+ if (neighbors.size() % 2 != 0) {
+ return false;
+ }
+ }
+
+ return isCoherentlyConnected();
+ }
+
+ public List findEulerianCircuit() {
+ if (!hasEulerianCircuit()) {
+ return Collections.emptyList();
+ }
+
+ Map> tempGraph = new HashMap<>();
+ for (Map.Entry> entry : graph.entrySet()) {
+ tempGraph.put(entry.getKey(), new LinkedList<>(entry.getValue()));
+ }
+
+ Stack currentPath = new Stack<>();
+ LinkedList circuit = new LinkedList<>();
+
+ int startVertex = -1;
+ for (Map.Entry> entry : tempGraph.entrySet()) {
+ if (!entry.getValue().isEmpty()) {
+ startVertex = entry.getKey();
+ break;
+ }
+ }
+
+ if (startVertex == -1) {
+ if (graph.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(graph.keySet().iterator().next());
+ }
+
+ currentPath.push(startVertex);
+
+ while (!currentPath.isEmpty()) {
+ int currentVertex = currentPath.peek();
+
+ if (tempGraph.containsKey(currentVertex) && !tempGraph.get(currentVertex).isEmpty()) {
+ int nextVertex = tempGraph.get(currentVertex).pollFirst();
+ tempGraph.get(nextVertex).remove(Integer.valueOf(currentVertex));
+ currentPath.push(nextVertex);
+ } else {
+ circuit.addFirst(currentVertex);
+ currentPath.pop();
+ }
+ }
+
+ return circuit;
+ }
+
+ private boolean isCoherentlyConnected() {
+ if (graph.isEmpty()) {
+ return true;
+ }
+
+ Set visited = new HashSet<>();
+ int startNode = -1;
+
+ for (Map.Entry> entry : graph.entrySet()) {
+ if (!entry.getValue().isEmpty()) {
+ startNode = entry.getKey();
+ break;
+ }
+ }
+
+ if (startNode == -1) {
+ return true;
+ }
+
+ dfs(startNode, visited);
+
+ for (Map.Entry> entry : graph.entrySet()) {
+ if (!entry.getValue().isEmpty() && !visited.contains(entry.getKey())) {
+ return false;
+ }
+ }
+ 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..4dadb206d134
--- /dev/null
+++ b/src/test/java/com/thealgorithms/graph/HierholzerAlgorithmTest.java
@@ -0,0 +1,60 @@
+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 org.junit.jupiter.api.Test;
+
+public class HierholzerAlgorithmTest {
+
+ @Test
+ public void testFindsEulerianCircuitInSimpleTriangleGraph() {
+ 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);
+ assertTrue(algorithm.hasEulerianCircuit());
+ List circuit = algorithm.findEulerianCircuit();
+ assertEquals(4, circuit.size());
+ assertEquals(circuit.get(0), circuit.get(circuit.size() - 1));
+ }
+
+ @Test
+ public void testFailsForGraphWithOddDegreeVertices() {
+ 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);
+ assertFalse(algorithm.hasEulerianCircuit());
+ assertTrue(algorithm.findEulerianCircuit().isEmpty());
+ }
+
+ @Test
+ public void testFailsForDisconnectedGraph() {
+ 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);
+ assertFalse(algorithm.hasEulerianCircuit());
+ }
+
+ @Test
+ public void testHandlesEmptyGraph() {
+ Map> graph = new HashMap<>();
+ HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
+ assertTrue(algorithm.hasEulerianCircuit());
+ assertTrue(algorithm.findEulerianCircuit().isEmpty());
+ }
+}