Skip to content
126 changes: 126 additions & 0 deletions src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
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;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add proper JavaDoc comments explaining this algorithm, its purpose, and expected input (e.g., undirected graph), similar to other classes in the repository.

public class HierholzerAlgorithm {

private final Map<Integer, LinkedList<Integer>> graph;

public HierholzerAlgorithm(Map<Integer, LinkedList<Integer>> graph) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider mentioning in JavaDoc that an undirected graph is expected, if it is so.

this.graph = (graph == null) ? new HashMap<>() : graph;
}

public boolean hasEulerianCircuit() {
if (graph.isEmpty()) {
return true;
}

// FINAL FIX: Loop over values directly for efficiency.
for (List<Integer> neighbors : graph.values()) {
if (neighbors.size() % 2 != 0) {
return false;
}
}

if (!isCoherentlyConnected()) {
return false;
}

return true;
}

public List<Integer> findEulerianCircuit() {
if (!hasEulerianCircuit()) {
return Collections.emptyList();
}

Map<Integer, LinkedList<Integer>> tempGraph = new HashMap<>();
for (Map.Entry<Integer, LinkedList<Integer>> entry : graph.entrySet()) {
tempGraph.put(entry.getKey(), new LinkedList<>(entry.getValue()));
}

Stack<Integer> currentPath = new Stack<>();
List<Integer> circuit = new LinkedList<>();

int startVertex = -1;
// FINAL FIX: Use entrySet for efficiency.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why "FINAL FIX:" ? I think this comment might be confusing...

for (Map.Entry<Integer, LinkedList<Integer>> 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.add(0, currentPath.pop());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using LinkedList.addFirst() for better performance.

}
}

return circuit;
}

private boolean isCoherentlyConnected() {
if (graph.isEmpty()) {
return true;
}

Set<Integer> visited = new HashSet<>();
int startNode = -1;

// FINAL FIX: Use entrySet for efficiency.
for (Map.Entry<Integer, LinkedList<Integer>> entry : graph.entrySet()) {
if (!entry.getValue().isEmpty()) {
startNode = entry.getKey();
break;
}
}

if (startNode == -1) {
return true;
}

dfs(startNode, visited);

// FINAL FIX: Use entrySet for efficiency.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why "FINAL FIX:" ? I think this comment might be confusing...

for (Map.Entry<Integer, LinkedList<Integer>> entry : graph.entrySet()) {
if (!entry.getValue().isEmpty() && !visited.contains(entry.getKey())) {
return false;
}
}
return true;
}

private void dfs(int u, Set<Integer> visited) {
visited.add(u);
if (graph.containsKey(u)) {
for (int v : graph.get(u)) {
if (!visited.contains(v)) {
dfs(v, visited);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Integer, LinkedList<Integer>> 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<Integer> circuit = algorithm.findEulerianCircuit();
assertEquals(4, circuit.size());
assertEquals(circuit.get(0), circuit.get(circuit.size() - 1));
}

@Test
public void testFailsForGraphWithOddDegreeVertices() {
Map<Integer, LinkedList<Integer>> 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<Integer, LinkedList<Integer>> 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<Integer, LinkedList<Integer>> graph = new HashMap<>();
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
assertTrue(algorithm.hasEulerianCircuit());
assertTrue(algorithm.findEulerianCircuit().isEmpty());
}
}