Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>13.3.0</version>
<version>13.4.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.9.8.2</version>
<version>4.9.8.3</version>
<configuration>
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
<includeTests>true</includeTests>
Expand Down
122 changes: 122 additions & 0 deletions src/main/java/com/thealgorithms/graph/TarjanBridges.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.thealgorithms.graph;

import java.util.ArrayList;
import java.util.List;

/**
* Implementation of Tarjan's Bridge-Finding Algorithm for undirected graphs.
*
* <p>A <b>bridge</b> (also called a cut-edge) is an edge in an undirected graph whose removal
* increases the number of connected components. Bridges represent critical links
* in a network — if any bridge is removed, part of the network becomes unreachable.</p>
*
* <p>The algorithm performs a single Depth-First Search (DFS) traversal, tracking two
* values for each vertex:</p>
* <ul>
* <li><b>discoveryTime</b> — the time step at which the vertex was first visited.</li>
* <li><b>lowLink</b> — the smallest discovery time reachable from the subtree rooted
* at that vertex (via back edges).</li>
* </ul>
*
* <p>An edge (u, v) is a bridge if and only if {@code lowLink[v] > discoveryTime[u]},
* meaning there is no back edge from the subtree of v that can reach u or any ancestor of u.</p>
*
* <p>Time Complexity: O(V + E), where V is the number of vertices and E is the number of edges.</p>
* <p>Space Complexity: O(V + E) for the adjacency list, discovery/low arrays, and recursion stack.</p>
*
* @see <a href="https://en.wikipedia.org/wiki/Bridge_(graph_theory)">Wikipedia: Bridge (graph theory)</a>
*/
public final class TarjanBridges {

private TarjanBridges() {
throw new UnsupportedOperationException("Utility class");
}

/**
* Finds all bridge edges in an undirected graph.
*
* <p>The graph is represented as an adjacency list where each vertex is identified by
* an integer in the range {@code [0, vertexCount)}. For each undirected edge (u, v),
* v must appear in {@code adjacencyList.get(u)} and u must appear in
* {@code adjacencyList.get(v)}.</p>
*
* @param vertexCount the total number of vertices in the graph (must be non-negative)
* @param adjacencyList the adjacency list representation of the graph; must contain
* exactly {@code vertexCount} entries (one per vertex)
* @return a list of bridge edges, where each bridge is represented as an {@code int[]}
* of length 2 with {@code edge[0] < edge[1]}; returns an empty list if no bridges exist
* @throws IllegalArgumentException if {@code vertexCount} is negative, or if
* {@code adjacencyList} is null or its size does not match
* {@code vertexCount}
*/
public static List<int[]> findBridges(int vertexCount, List<List<Integer>> adjacencyList) {
if (vertexCount < 0) {
throw new IllegalArgumentException("vertexCount must be non-negative");
}
if (adjacencyList == null || adjacencyList.size() != vertexCount) {
throw new IllegalArgumentException("adjacencyList size must equal vertexCount");
}

List<int[]> bridges = new ArrayList<>();

if (vertexCount == 0) {
return bridges;
}

BridgeFinder finder = new BridgeFinder(vertexCount, adjacencyList, bridges);

// Run DFS from every unvisited vertex to handle disconnected graphs
for (int i = 0; i < vertexCount; i++) {
if (!finder.visited[i]) {
finder.dfs(i, -1);
}
}

return bridges;
}

private static class BridgeFinder {
private final List<List<Integer>> adjacencyList;
private final List<int[]> bridges;
private final int[] discoveryTime;
private final int[] lowLink;
boolean[] visited;
private int timer;

BridgeFinder(int vertexCount, List<List<Integer>> adjacencyList, List<int[]> bridges) {
this.adjacencyList = adjacencyList;
this.bridges = bridges;
this.discoveryTime = new int[vertexCount];
this.lowLink = new int[vertexCount];
this.visited = new boolean[vertexCount];
this.timer = 0;
}

/**
* Performs DFS from the given vertex, computing discovery times and low-link values,
* and collects any bridge edges found.
*
* @param u the current vertex being explored
* @param parent the parent of u in the DFS tree (-1 if u is a root)
*/
void dfs(int u, int parent) {
visited[u] = true;
discoveryTime[u] = timer;
lowLink[u] = timer;
timer++;

for (int v : adjacencyList.get(u)) {
if (!visited[v]) {
dfs(v, u);
lowLink[u] = Math.min(lowLink[u], lowLink[v]);

if (lowLink[v] > discoveryTime[u]) {
bridges.add(new int[] {Math.min(u, v), Math.max(u, v)});
}
} else if (v != parent) {
lowLink[u] = Math.min(lowLink[u], discoveryTime[v]);
}
}
}
}
}
51 changes: 51 additions & 0 deletions src/main/java/com/thealgorithms/maths/Correlation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.thealgorithms.maths;

/**
* Class for correlation of two discrete variables
*/

public final class Correlation {
private Correlation() {
}

public static final double DELTA = 1e-9;

/**
* Discrete correlation function.
* Correlation between two discrete variables is calculated
* according to the formula: Cor(x, y)=Cov(x, y)/sqrt(Var(x)*Var(y)).
* Correlation with a constant variable is taken to be zero.
*
* @param x The first discrete variable
* @param y The second discrete variable
* @param n The number of values for each variable
* @return The result of the correlation of variables x,y.
*/
public static double correlation(double[] x, double[] y, int n) {
double exy = 0; // E(XY)
double ex = 0; // E(X)
double exx = 0; // E(X^2)
double ey = 0; // E(Y)
double eyy = 0; // E(Y^2)
for (int i = 0; i < n; i++) {
exy += x[i] * y[i];
ex += x[i];
exx += x[i] * x[i];
ey += y[i];
eyy += y[i] * y[i];
}
exy /= n;
ex /= n;
exx /= n;
ey /= n;
eyy /= n;
double cov = exy - ex * ey; // Cov(X, Y) = E(XY)-E(X)E(Y)
double varx = Math.sqrt(exx - ex * ex); // Var(X) = sqrt(E(X^2)-E(X)^2)
double vary = Math.sqrt(eyy - ey * ey); // Var(Y) = sqrt(E(Y^2)-E(Y)^2)
if (varx * vary < DELTA) { // Var(X) = 0 means X = const, the same about Y
return 0;
} else {
return cov / Math.sqrt(varx * vary);
}
}
}
18 changes: 7 additions & 11 deletions src/main/java/com/thealgorithms/maths/Factorial.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package com.thealgorithms.maths;

import java.math.BigInteger;

public final class Factorial {
private Factorial() {
}

/**
* Calculate factorial N using iteration
*
* @param n the number
* @return the factorial of {@code n}
*/
public static long factorial(int n) {
public static BigInteger factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException("Input number cannot be negative");
}
long factorial = 1;
for (int i = 1; i <= n; ++i) {
factorial *= i;
BigInteger result = BigInteger.ONE;
for (int i = 1; i <= n; i++) {
result = result.multiply(BigInteger.valueOf(i));
}
return factorial;
return result;
}
}
9 changes: 8 additions & 1 deletion src/main/java/com/thealgorithms/searches/BinarySearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,18 @@ class BinarySearch implements SearchAlgorithm {
*/
@Override
public <T extends Comparable<T>> int find(T[] array, T key) {
// Handle edge case: empty array
// Handle edge case: null or empty array
if (array == null || array.length == 0) {
return -1;
}

// Handle edge case: null key
// Searching for null in an array of Comparables is undefined behavior
// Return -1 to indicate not found rather than throwing NPE
if (key == null) {
return -1;
}

// Delegate to the core search implementation
return search(array, key, 0, array.length - 1);
}
Expand Down
40 changes: 32 additions & 8 deletions src/main/java/com/thealgorithms/searches/JumpSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,50 @@
* Once the range is found, a linear search is performed within that block.
*
* <p>
* The Jump Search algorithm is particularly effective for large sorted arrays where the cost of
* performing a linear search on the entire array would be prohibitive.
* <b>How it works:</b>
* <ol>
* <li>Calculate the optimal block size as √n (square root of array length)</li>
* <li>Jump ahead by the block size until the current element is greater than the target</li>
* <li>Perform a linear search backwards within the identified block</li>
* </ol>
*
* <p>
* Worst-case performance: O(√N)<br>
* Best-case performance: O(1)<br>
* Average performance: O(√N)<br>
* Worst-case space complexity: O(1)
* <b>Example:</b><br>
* Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19], Target: 9<br>
* Step 1: Jump from index 0 → 3 → 6 (9 < 13, so we found the block)<br>
* Step 2: Linear search from index 3 to 6: found 9 at index 4<br>
* Result: Index = 4
*
* <p>
* <b>Time Complexity:</b><br>
* - Best-case: O(1) - element found at first position<br>
* - Average: O(√n) - optimal block size reduces jumps<br>
* - Worst-case: O(√n) - element at end of array or not present<br>
*
* <p>
* <b>Space Complexity:</b> O(1) - only uses a constant amount of extra space
*
* <p>
* <b>Note:</b> Jump Search requires a sorted array. For unsorted arrays, use Linear Search.
* Compared to Linear Search (O(n)), Jump Search is faster for large arrays.
* Compared to Binary Search (O(log n)), Jump Search is less efficient but may be
* preferable when jumping through a linked list or when backward scanning is costly.
*
* <p>
* This class implements the {@link SearchAlgorithm} interface, providing a generic search method
* for any comparable type.
*
* @see SearchAlgorithm
* @see BinarySearch
* @see LinearSearch
*/
public class JumpSearch implements SearchAlgorithm {

/**
* Jump Search algorithm implementation.
*
* @param array the sorted array containing elements
* @param key the element to be searched
* @param array the sorted array containing elements (must be sorted in ascending order)
* @param key the element to be searched for
* @return the index of {@code key} if found, otherwise -1
*/
@Override
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/thealgorithms/searches/LinearSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ public class LinearSearch implements SearchAlgorithm {
*
* @param array List to be searched
* @param value Key being searched for
* @return Location of the key
* @return Location of the key, -1 if array is null or empty, or key not found
*/
@Override
public <T extends Comparable<T>> int find(T[] array, T value) {
if (array == null || array.length == 0) {
return -1;
}
for (int i = 0; i < array.length; i++) {
if (array[i].compareTo(value) == 0) {
return i;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public <T extends Comparable<T>> int find(T[] array, T key) {

int i = 0;
// Search without bound checking since sentinel guarantees we'll find the key
while (array[i].compareTo(key) != 0) {
// Null check for array element to prevent NPE when array contains null elements
while (array[i] != null && array[i].compareTo(key) != 0) {
i++;
}

Expand Down
Loading