Skip to content

Commit 96d4e1e

Browse files
Merge branch 'master' into add-shared-config-module
2 parents e8c41ac + d717ca4 commit 96d4e1e

File tree

8 files changed

+402
-2
lines changed

8 files changed

+402
-2
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
- 📄 [FordFulkerson](src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java)
174174
- 📄 [Graphs](src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java)
175175
- 📄 [HamiltonianCycle](src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java)
176+
- 📄 [HierholzerAlgorithm](src/main/java/com/thealgorithms/graph/HierholzerAlgorithm.java)
176177
- 📄 [JohnsonsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java)
177178
- 📄 [KahnsAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java)
178179
- 📄 [Kosaraju](src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java)

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<dependency>
2121
<groupId>org.junit</groupId>
2222
<artifactId>junit-bom</artifactId>
23-
<version>6.0.0</version>
23+
<version>6.0.1</version>
2424
<type>pom</type>
2525
<scope>import</scope>
2626
</dependency>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.thealgorithms.graph;
2+
3+
import java.util.Collections;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
import java.util.LinkedList;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Set;
10+
import java.util.Stack;
11+
12+
/**
13+
* Implementation of Hierholzer's algorithm to find an Eulerian Circuit in an undirected graph.
14+
* <p>
15+
* An Eulerian circuit is a trail in a graph that visits every edge exactly once,
16+
* starting and ending at the same vertex. This algorithm finds such a circuit if one exists.
17+
* </p>
18+
* <p>
19+
* This implementation is designed for an <strong>undirected graph</strong>. For a valid Eulerian
20+
* circuit to exist, the graph must satisfy two conditions:
21+
* <ol>
22+
* <li>All vertices with a non-zero degree must be part of a single connected component.</li>
23+
* <li>Every vertex must have an even degree (an even number of edges connected to it).</li>
24+
* </ol>
25+
* </p>
26+
* <p>
27+
* The algorithm runs in O(E + V) time, where E is the number of edges and V is the number of vertices.
28+
* The graph is represented by a Map where keys are vertices and values are a LinkedList of adjacent vertices.
29+
* </p>
30+
*
31+
* @see <a href="https://en.wikipedia.org/wiki/Eulerian_path#Hierholzer's_algorithm">Wikipedia: Hierholzer's algorithm</a>
32+
*/
33+
public final class HierholzerAlgorithm {
34+
35+
private final Map<Integer, LinkedList<Integer>> graph;
36+
37+
public HierholzerAlgorithm(Map<Integer, LinkedList<Integer>> graph) {
38+
this.graph = (graph == null) ? new HashMap<>() : graph;
39+
}
40+
41+
public boolean hasEulerianCircuit() {
42+
if (graph.isEmpty()) {
43+
return true;
44+
}
45+
46+
for (List<Integer> neighbors : graph.values()) {
47+
if (neighbors.size() % 2 != 0) {
48+
return false;
49+
}
50+
}
51+
52+
return isCoherentlyConnected();
53+
}
54+
55+
public List<Integer> findEulerianCircuit() {
56+
if (!hasEulerianCircuit()) {
57+
return Collections.emptyList();
58+
}
59+
60+
Map<Integer, LinkedList<Integer>> tempGraph = new HashMap<>();
61+
for (Map.Entry<Integer, LinkedList<Integer>> entry : graph.entrySet()) {
62+
tempGraph.put(entry.getKey(), new LinkedList<>(entry.getValue()));
63+
}
64+
65+
Stack<Integer> currentPath = new Stack<>();
66+
LinkedList<Integer> circuit = new LinkedList<>();
67+
68+
int startVertex = -1;
69+
for (Map.Entry<Integer, LinkedList<Integer>> entry : tempGraph.entrySet()) {
70+
if (!entry.getValue().isEmpty()) {
71+
startVertex = entry.getKey();
72+
break;
73+
}
74+
}
75+
76+
if (startVertex == -1) {
77+
if (graph.isEmpty()) {
78+
return Collections.emptyList();
79+
}
80+
return Collections.singletonList(graph.keySet().iterator().next());
81+
}
82+
83+
currentPath.push(startVertex);
84+
85+
while (!currentPath.isEmpty()) {
86+
int currentVertex = currentPath.peek();
87+
88+
if (tempGraph.containsKey(currentVertex) && !tempGraph.get(currentVertex).isEmpty()) {
89+
int nextVertex = tempGraph.get(currentVertex).pollFirst();
90+
tempGraph.get(nextVertex).remove(Integer.valueOf(currentVertex));
91+
currentPath.push(nextVertex);
92+
} else {
93+
circuit.addFirst(currentVertex);
94+
currentPath.pop();
95+
}
96+
}
97+
98+
return circuit;
99+
}
100+
101+
private boolean isCoherentlyConnected() {
102+
if (graph.isEmpty()) {
103+
return true;
104+
}
105+
106+
Set<Integer> visited = new HashSet<>();
107+
int startNode = -1;
108+
109+
for (Map.Entry<Integer, LinkedList<Integer>> entry : graph.entrySet()) {
110+
if (!entry.getValue().isEmpty()) {
111+
startNode = entry.getKey();
112+
break;
113+
}
114+
}
115+
116+
if (startNode == -1) {
117+
return true;
118+
}
119+
120+
dfs(startNode, visited);
121+
122+
for (Map.Entry<Integer, LinkedList<Integer>> entry : graph.entrySet()) {
123+
if (!entry.getValue().isEmpty() && !visited.contains(entry.getKey())) {
124+
return false;
125+
}
126+
}
127+
return true;
128+
}
129+
130+
private void dfs(int u, Set<Integer> visited) {
131+
visited.add(u);
132+
if (graph.containsKey(u)) {
133+
for (int v : graph.get(u)) {
134+
if (!visited.contains(v)) {
135+
dfs(v, visited);
136+
}
137+
}
138+
}
139+
}
140+
}

src/main/java/com/thealgorithms/maths/Area.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,25 @@ public static double surfaceAreaSphere(final double radius) {
4848
return 4 * Math.PI * radius * radius;
4949
}
5050

51+
/**
52+
* Calculate the surface area of a pyramid with a square base.
53+
*
54+
* @param sideLength side length of the square base
55+
* @param slantHeight slant height of the pyramid
56+
* @return surface area of the given pyramid
57+
*/
58+
public static double surfaceAreaPyramid(final double sideLength, final double slantHeight) {
59+
if (sideLength <= 0) {
60+
throw new IllegalArgumentException("Must be a positive sideLength");
61+
}
62+
if (slantHeight <= 0) {
63+
throw new IllegalArgumentException("Must be a positive slantHeight");
64+
}
65+
double baseArea = sideLength * sideLength;
66+
double lateralSurfaceArea = 2 * sideLength * slantHeight;
67+
return baseArea + lateralSurfaceArea;
68+
}
69+
5170
/**
5271
* Calculate the area of a rectangle.
5372
*

src/main/java/com/thealgorithms/maths/JugglerSequence.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static void jugglerSequence(int inputNumber) {
4343
seq.add(n + "");
4444
}
4545
String res = String.join(",", seq);
46-
System.out.println(res);
46+
System.out.print(res + "\n");
4747
}
4848

4949
// Driver code
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.thealgorithms.physics;
2+
3+
/**
4+
* Implements Coulomb's Law for electrostatics.
5+
* Provides simple static methods to calculate electrostatic force and circular orbit velocity.
6+
*
7+
* @author [Priyanshu Singh](https://github.com/Priyanshu1303d)
8+
* @see <a href="https://en.wikipedia.org/wiki/Coulomb%27s_law">Wikipedia</a>
9+
*/
10+
public final class CoulombsLaw {
11+
12+
/** Coulomb's constant in N·m²/C² */
13+
public static final double COULOMBS_CONSTANT = 8.9875517923e9;
14+
15+
/**
16+
* Private constructor to prevent instantiation of this utility class.
17+
*/
18+
private CoulombsLaw() {
19+
}
20+
21+
/**
22+
* Calculates the electrostatic force vector exerted by one charge on another.
23+
* The returned vector is the force *on* the second charge (q2).
24+
*
25+
* @param q1 Charge of the first particle (in Coulombs).
26+
* @param x1 X-position of the first particle (m).
27+
* @param y1 Y-position of the first particle (m).
28+
* @param q2 Charge of the second particle (in Coulombs).
29+
* @param x2 X-position of the second particle (m).
30+
* @param y2 Y-position of the second particle (m).
31+
* @return A double array `[fx, fy]` representing the force vector on the second charge.
32+
*/
33+
public static double[] calculateForceVector(double q1, double x1, double y1, double q2, double x2, double y2) {
34+
// Vector from 1 to 2
35+
double dx = x2 - x1;
36+
double dy = y2 - y1;
37+
double distanceSq = dx * dx + dy * dy;
38+
39+
// If particles are at the same position, force is zero to avoid division by zero.
40+
if (distanceSq == 0) {
41+
return new double[] {0, 0};
42+
}
43+
44+
double distance = Math.sqrt(distanceSq);
45+
// Force magnitude: k * (q1 * q2) / r^2
46+
// A positive result is repulsive (pushes q2 away from q1).
47+
// A negative result is attractive (pulls q2 toward q1).
48+
double forceMagnitude = COULOMBS_CONSTANT * q1 * q2 / distanceSq;
49+
50+
// Calculate the components of the force vector
51+
// (dx / distance) is the unit vector pointing from 1 to 2.
52+
double fx = forceMagnitude * (dx / distance);
53+
double fy = forceMagnitude * (dy / distance);
54+
55+
return new double[] {fx, fy};
56+
}
57+
58+
/**
59+
* Calculates the speed required for a stable circular orbit of a charged particle
60+
* around a central charge (e.g., an electron orbiting a nucleus).
61+
*
62+
* @param centralCharge The charge of the central body (in Coulombs).
63+
* @param orbitingCharge The charge of the orbiting body (in Coulombs).
64+
* @param orbitingMass The mass of the orbiting body (in kg).
65+
* @param radius The radius of the orbit (in m).
66+
* @return The orbital speed (in m/s).
67+
* @throws IllegalArgumentException if mass or radius are not positive.
68+
*/
69+
public static double calculateCircularOrbitVelocity(double centralCharge, double orbitingCharge, double orbitingMass, double radius) {
70+
if (orbitingMass <= 0 || radius <= 0) {
71+
throw new IllegalArgumentException("Orbiting mass and radius must be positive.");
72+
}
73+
74+
// We only need the magnitude of the force, which is always positive.
75+
double forceMagnitude = Math.abs(COULOMBS_CONSTANT * centralCharge * orbitingCharge) / (radius * radius);
76+
77+
// F_c = m * v^2 / r => v = sqrt(F_c * r / m)
78+
return Math.sqrt(forceMagnitude * radius / orbitingMass);
79+
}
80+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.thealgorithms.graph;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.util.Arrays;
8+
import java.util.Collections;
9+
import java.util.HashMap;
10+
import java.util.LinkedList;
11+
import java.util.List;
12+
import java.util.Map;
13+
import org.junit.jupiter.api.Test;
14+
15+
public class HierholzerAlgorithmTest {
16+
17+
@Test
18+
public void testFindsEulerianCircuitInSimpleTriangleGraph() {
19+
Map<Integer, LinkedList<Integer>> graph = new HashMap<>();
20+
graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
21+
graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
22+
graph.put(2, new LinkedList<>(Arrays.asList(0, 1)));
23+
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
24+
assertTrue(algorithm.hasEulerianCircuit());
25+
List<Integer> circuit = algorithm.findEulerianCircuit();
26+
assertEquals(4, circuit.size());
27+
assertEquals(circuit.get(0), circuit.get(circuit.size() - 1));
28+
}
29+
30+
@Test
31+
public void testFailsForGraphWithOddDegreeVertices() {
32+
Map<Integer, LinkedList<Integer>> graph = new HashMap<>();
33+
graph.put(0, new LinkedList<>(Collections.singletonList(1)));
34+
graph.put(1, new LinkedList<>(Collections.singletonList(0)));
35+
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
36+
assertFalse(algorithm.hasEulerianCircuit());
37+
assertTrue(algorithm.findEulerianCircuit().isEmpty());
38+
}
39+
40+
@Test
41+
public void testFailsForDisconnectedGraph() {
42+
Map<Integer, LinkedList<Integer>> graph = new HashMap<>();
43+
graph.put(0, new LinkedList<>(Arrays.asList(1, 2)));
44+
graph.put(1, new LinkedList<>(Arrays.asList(0, 2)));
45+
graph.put(2, new LinkedList<>(Arrays.asList(0, 1)));
46+
graph.put(3, new LinkedList<>(Arrays.asList(4, 5)));
47+
graph.put(4, new LinkedList<>(Arrays.asList(3, 5)));
48+
graph.put(5, new LinkedList<>(Arrays.asList(3, 4)));
49+
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
50+
assertFalse(algorithm.hasEulerianCircuit());
51+
}
52+
53+
@Test
54+
public void testHandlesEmptyGraph() {
55+
Map<Integer, LinkedList<Integer>> graph = new HashMap<>();
56+
HierholzerAlgorithm algorithm = new HierholzerAlgorithm(graph);
57+
assertTrue(algorithm.hasEulerianCircuit());
58+
assertTrue(algorithm.findEulerianCircuit().isEmpty());
59+
}
60+
}

0 commit comments

Comments
 (0)