-
Notifications
You must be signed in to change notification settings - Fork 20.5k
feat: Add Hierholzer's Algorithm for Eulerian Circuits #6726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 7 commits
d3b0e3b
568913c
456e0cf
96bd4fc
0fac806
7647a8d
accbbfb
1de236e
ef458a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
|
||
public class HierholzerAlgorithm { | ||
|
||
private final Map<Integer, LinkedList<Integer>> graph; | ||
|
||
public HierholzerAlgorithm(Map<Integer, LinkedList<Integer>> graph) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
||
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()); | ||
|
||
} | ||
} | ||
|
||
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. | ||
|
||
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()); | ||
} | ||
} |
There was a problem hiding this comment.
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.