Skip to content

Commit 60098bb

Browse files
committed
Added Hierholzer Algorith to find Eulerian Path
1 parent 2b547db commit 60098bb

File tree

1 file changed

+59
-61
lines changed

1 file changed

+59
-61
lines changed

src/main/java/com/thealgorithms/graph/HierholzerEulerianPath.java

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,44 @@ public List<Integer> findEulerianPath() {
112112

113113
int[] inDegree = new int[n];
114114
int[] outDegree = new int[n];
115-
int edgeCount = 0;
115+
int edgeCount = computeDegrees(inDegree, outDegree);
116+
117+
// no edges -> single vertex response requested by tests: [0]
118+
if (edgeCount == 0) {
119+
return Collections.singletonList(0);
120+
}
121+
122+
int startNode = determineStartNode(inDegree, outDegree);
123+
if (startNode == -1) return new ArrayList<>();
124+
125+
if (!allNonZeroDegreeVerticesWeaklyConnected(startNode, n, outDegree, inDegree)) {
126+
return new ArrayList<>();
127+
}
128+
129+
List<Integer> path = buildHierholzerPath(startNode, n);
130+
if (path.size() != edgeCount + 1) return new ArrayList<>();
131+
132+
return rotateEulerianCircuitIfNeeded(path, outDegree, inDegree);
133+
}
116134

117-
// compute degrees and total edges
118-
for (int u = 0; u < n; u++) {
135+
private int computeDegrees(int[] inDegree, int[] outDegree) {
136+
int edgeCount = 0;
137+
for (int u = 0; u < graph.getNumNodes(); u++) {
119138
for (int v : graph.getEdges(u)) {
120139
outDegree[u]++;
121140
inDegree[v]++;
122141
edgeCount++;
123142
}
124143
}
144+
return edgeCount;
145+
}
125146

126-
// no edges -> single vertex response requested by tests: [0]
127-
if (edgeCount == 0) {
128-
// If there is at least one vertex, tests expect [0] for single-node graphs with no edges.
129-
// For n >= 1, return [0]. (Tests create Graph(1) for that case.)
130-
return Collections.singletonList(0);
131-
}
132-
133-
// Check degree differences to determine Eulerian path/circuit possibility
147+
private int determineStartNode(int[] inDegree, int[] outDegree) {
148+
int n = graph.getNumNodes();
134149
int startNode = -1;
135150
int startCount = 0;
136151
int endCount = 0;
152+
137153
for (int i = 0; i < n; i++) {
138154
int diff = outDegree[i] - inDegree[i];
139155
if (diff == 1) {
@@ -142,16 +158,14 @@ public List<Integer> findEulerianPath() {
142158
} else if (diff == -1) {
143159
endCount++;
144160
} else if (Math.abs(diff) > 1) {
145-
return new ArrayList<>(); // invalid degree difference
161+
return -1;
146162
}
147163
}
148164

149-
// Must be either exactly one start and one end (path) or zero of both (circuit)
150165
if (!((startCount == 1 && endCount == 1) || (startCount == 0 && endCount == 0))) {
151-
return new ArrayList<>();
166+
return -1;
152167
}
153168

154-
// If circuit, choose smallest-index vertex with outgoing edges (deterministic for tests)
155169
if (startNode == -1) {
156170
for (int i = 0; i < n; i++) {
157171
if (outDegree[i] > 0) {
@@ -160,79 +174,67 @@ public List<Integer> findEulerianPath() {
160174
}
161175
}
162176
}
177+
return startNode;
178+
}
163179

164-
if (startNode == -1) {
165-
return new ArrayList<>();
166-
}
167-
168-
// Weak connectivity check: every vertex with non-zero degree must be in the same weak component.
169-
if (!allNonZeroDegreeVerticesWeaklyConnected(startNode, n, outDegree, inDegree)) {
170-
return new ArrayList<>();
171-
}
172-
173-
// Create modifiable adjacency structure for traversal
180+
private List<Integer> buildHierholzerPath(int startNode, int n) {
174181
List<Deque<Integer>> tempAdj = new ArrayList<>();
175182
for (int i = 0; i < n; i++) {
176183
tempAdj.add(new ArrayDeque<>(graph.getEdges(i)));
177184
}
178185

179-
// Hierholzer's traversal using stack
180186
Deque<Integer> stack = new ArrayDeque<>();
181187
List<Integer> path = new ArrayList<>();
182188
stack.push(startNode);
183189

184190
while (!stack.isEmpty()) {
185191
int u = stack.peek();
186192
if (!tempAdj.get(u).isEmpty()) {
187-
int v = tempAdj.get(u).pollFirst();
188-
stack.push(v);
193+
stack.push(tempAdj.get(u).pollFirst());
189194
} else {
190195
path.add(stack.pop());
191196
}
192197
}
193198

194-
// Path is recorded in reverse
195199
Collections.reverse(path);
200+
return path;
201+
}
196202

197-
// Ensure all edges were used
198-
if (path.size() != edgeCount + 1) {
199-
return new ArrayList<>();
203+
private List<Integer> rotateEulerianCircuitIfNeeded(List<Integer> path, int[] outDegree, int[] inDegree) {
204+
int startCount = 0;
205+
int endCount = 0;
206+
for (int i = 0; i < outDegree.length; i++) {
207+
int diff = outDegree[i] - inDegree[i];
208+
if (diff == 1)
209+
startCount++;
210+
else if (diff == -1)
211+
endCount++;
200212
}
201213

202-
// If Eulerian circuit (startCount==0 && endCount==0), rotate path so it starts at
203-
// the smallest-index vertex that has outgoing edges (deterministic expected by tests)
204-
if (startCount == 0 && endCount == 0) {
214+
if (startCount == 0 && endCount == 0 && !path.isEmpty()) {
205215
int preferredStart = -1;
206-
for (int i = 0; i < n; i++) {
216+
for (int i = 0; i < outDegree.length; i++) {
207217
if (outDegree[i] > 0) {
208218
preferredStart = i;
209219
break;
210220
}
211221
}
212-
if (preferredStart != -1 && !path.isEmpty()) {
213-
if (path.get(0) != preferredStart) {
214-
// find index where preferredStart occurs and rotate
215-
int idx = -1;
216-
for (int i = 0; i < path.size(); i++) {
217-
if (path.get(i) == preferredStart) {
218-
idx = i;
219-
break;
220-
}
221-
}
222-
if (idx > 0) {
223-
List<Integer> rotated = new ArrayList<>();
224-
for (int i = idx; i < path.size(); i++) {
225-
rotated.add(path.get(i));
226-
}
227-
for (int i = 1; i <= idx; i++) {
228-
rotated.add(path.get(i % path.size()));
229-
}
230-
path = rotated;
231-
}
222+
223+
if (preferredStart != -1 && path.get(0) != preferredStart) {
224+
int idx = 0;
225+
for (int node : path) {
226+
if (node == preferredStart) break;
227+
idx++;
228+
}
229+
230+
if (idx > 0) {
231+
List<Integer> rotated = new ArrayList<>();
232+
for (int i = idx; i < path.size(); i++) rotated.add(path.get(i));
233+
for (int i = 0; i < idx; i++) rotated.add(path.get(i));
234+
path = rotated;
232235
}
233236
}
234237
}
235-
236238
return path;
237239
}
238240

@@ -251,17 +253,14 @@ private boolean allNonZeroDegreeVerticesWeaklyConnected(int startNode, int n, in
251253
stack.push(startNode);
252254
visited[startNode] = true;
253255

254-
// Build undirected adjacency on the fly: for each u -> v, consider u - v
255256
while (!stack.isEmpty()) {
256257
int u = stack.pop();
257-
// neighbors: outgoing edges
258258
for (int v : graph.getEdges(u)) {
259259
if (!visited[v]) {
260260
visited[v] = true;
261261
stack.push(v);
262262
}
263263
}
264-
// neighbors: incoming edges (we must scan all vertices to find incoming edges)
265264
for (int x = 0; x < n; x++) {
266265
if (!visited[x]) {
267266
for (int y : graph.getEdges(x)) {
@@ -275,7 +274,6 @@ private boolean allNonZeroDegreeVerticesWeaklyConnected(int startNode, int n, in
275274
}
276275
}
277276

278-
// check all vertices with non-zero degree are visited
279277
for (int i = 0; i < n; i++) {
280278
if (outDegree[i] + inDegree[i] > 0 && !visited[i]) {
281279
return false;

0 commit comments

Comments
 (0)