From 92d47022e72fa00d37c00df35bbc7b123a87e7b5 Mon Sep 17 00:00:00 2001 From: Senrian <47714364+Senrian@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:52:54 +0800 Subject: [PATCH 01/10] fix: handle null and empty array in LinearSearch (issue #7340) (#7347) --- src/main/java/com/thealgorithms/searches/LinearSearch.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/searches/LinearSearch.java b/src/main/java/com/thealgorithms/searches/LinearSearch.java index 6403749a3154..00fb9c2d0fcf 100644 --- a/src/main/java/com/thealgorithms/searches/LinearSearch.java +++ b/src/main/java/com/thealgorithms/searches/LinearSearch.java @@ -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 > 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; From 227355e313627fb0394914cd7255c83c0e469beb Mon Sep 17 00:00:00 2001 From: Keykyrios Date: Sat, 28 Mar 2026 15:26:47 +0530 Subject: [PATCH 02/10] feat(graph): implement Tarjan's Bridge-Finding Algorithm (#7346) * feat: implement Tarjan's Bridge-Finding Algorithm Adds a classic graph algorithm to find bridge edges in an undirected graph in O(V + E) time. * style: format 2D arrays in TarjanBridgesTest --------- Co-authored-by: Deniz Altunkapan --- .../thealgorithms/graph/TarjanBridges.java | 122 +++++++++++ .../graph/TarjanBridgesTest.java | 207 ++++++++++++++++++ 2 files changed, 329 insertions(+) create mode 100644 src/main/java/com/thealgorithms/graph/TarjanBridges.java create mode 100644 src/test/java/com/thealgorithms/graph/TarjanBridgesTest.java diff --git a/src/main/java/com/thealgorithms/graph/TarjanBridges.java b/src/main/java/com/thealgorithms/graph/TarjanBridges.java new file mode 100644 index 000000000000..dbe2e710429a --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/TarjanBridges.java @@ -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. + * + *

A bridge (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.

+ * + *

The algorithm performs a single Depth-First Search (DFS) traversal, tracking two + * values for each vertex:

+ *
    + *
  • discoveryTime — the time step at which the vertex was first visited.
  • + *
  • lowLink — the smallest discovery time reachable from the subtree rooted + * at that vertex (via back edges).
  • + *
+ * + *

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.

+ * + *

Time Complexity: O(V + E), where V is the number of vertices and E is the number of edges.

+ *

Space Complexity: O(V + E) for the adjacency list, discovery/low arrays, and recursion stack.

+ * + * @see Wikipedia: Bridge (graph theory) + */ +public final class TarjanBridges { + + private TarjanBridges() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Finds all bridge edges in an undirected graph. + * + *

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)}.

+ * + * @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 findBridges(int vertexCount, List> 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 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> adjacencyList; + private final List bridges; + private final int[] discoveryTime; + private final int[] lowLink; + boolean[] visited; + private int timer; + + BridgeFinder(int vertexCount, List> adjacencyList, List 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]); + } + } + } + } +} diff --git a/src/test/java/com/thealgorithms/graph/TarjanBridgesTest.java b/src/test/java/com/thealgorithms/graph/TarjanBridgesTest.java new file mode 100644 index 000000000000..8608bfb2dfc9 --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/TarjanBridgesTest.java @@ -0,0 +1,207 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link TarjanBridges}. + * + *

Tests cover a wide range of graph configurations including simple graphs, + * cycles, trees, disconnected components, multigraph-like structures, and + * various edge cases to ensure correct bridge detection.

+ */ +class TarjanBridgesTest { + + /** + * Helper to build a symmetric adjacency list for an undirected graph. + */ + private static List> buildGraph(int vertexCount, int[][] edges) { + List> adj = new ArrayList<>(); + for (int i = 0; i < vertexCount; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + return adj; + } + + /** + * Sorts bridges for deterministic comparison. + */ + private static void sortBridges(List bridges) { + bridges.sort(Comparator.comparingInt((int[] a) -> a[0]).thenComparingInt(a -> a[1])); + } + + @Test + void testSimpleGraphWithOneBridge() { + // Graph: 0-1-2-3 where 1-2 is the only bridge + // 0---1---2---3 + // | | + // +-------+ (via 0-2 would make cycle, but not here) + // Actually: 0-1 in a cycle with 0-1, and 2-3 in a cycle with 2-3 + // Let's use: 0--1--2 (linear chain). All edges are bridges. + List> adj = buildGraph(3, new int[][] {{0, 1}, {1, 2}}); + List bridges = TarjanBridges.findBridges(3, adj); + sortBridges(bridges); + assertEquals(2, bridges.size()); + assertEquals(0, bridges.get(0)[0]); + assertEquals(1, bridges.get(0)[1]); + assertEquals(1, bridges.get(1)[0]); + assertEquals(2, bridges.get(1)[1]); + } + + @Test + void testCycleGraphHasNoBridges() { + // Graph: 0-1-2-0 (triangle). No bridges. + List> adj = buildGraph(3, new int[][] {{0, 1}, {1, 2}, {2, 0}}); + List bridges = TarjanBridges.findBridges(3, adj); + assertTrue(bridges.isEmpty()); + } + + @Test + void testTreeGraphAllEdgesAreBridges() { + // Tree: 0 + // / \ + // 1 2 + // / \ + // 3 4 + List> adj = buildGraph(5, new int[][] {{0, 1}, {0, 2}, {1, 3}, {1, 4}}); + List bridges = TarjanBridges.findBridges(5, adj); + assertEquals(4, bridges.size()); + } + + @Test + void testGraphWithMixedBridgesAndCycles() { + // Graph: + // 0---1 + // | | + // 3---2---4---5 + // | + // 6 + // Cycle: 0-1-2-3-0 (no bridges within) + // Bridges: 2-4, 4-5, 5-6 + List> adj = buildGraph(7, new int[][] {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {2, 4}, {4, 5}, {5, 6}}); + List bridges = TarjanBridges.findBridges(7, adj); + sortBridges(bridges); + assertEquals(3, bridges.size()); + assertEquals(2, bridges.get(0)[0]); + assertEquals(4, bridges.get(0)[1]); + assertEquals(4, bridges.get(1)[0]); + assertEquals(5, bridges.get(1)[1]); + assertEquals(5, bridges.get(2)[0]); + assertEquals(6, bridges.get(2)[1]); + } + + @Test + void testDisconnectedGraphWithBridges() { + // Component 1: 0-1 (bridge) + // Component 2: 2-3-4-2 (cycle, no bridges) + List> adj = buildGraph(5, new int[][] {{0, 1}, {2, 3}, {3, 4}, {4, 2}}); + List bridges = TarjanBridges.findBridges(5, adj); + assertEquals(1, bridges.size()); + assertEquals(0, bridges.get(0)[0]); + assertEquals(1, bridges.get(0)[1]); + } + + @Test + void testSingleVertex() { + List> adj = buildGraph(1, new int[][] {}); + List bridges = TarjanBridges.findBridges(1, adj); + assertTrue(bridges.isEmpty()); + } + + @Test + void testTwoVerticesWithOneEdge() { + List> adj = buildGraph(2, new int[][] {{0, 1}}); + List bridges = TarjanBridges.findBridges(2, adj); + assertEquals(1, bridges.size()); + assertEquals(0, bridges.get(0)[0]); + assertEquals(1, bridges.get(0)[1]); + } + + @Test + void testEmptyGraph() { + List> adj = buildGraph(0, new int[][] {}); + List bridges = TarjanBridges.findBridges(0, adj); + assertTrue(bridges.isEmpty()); + } + + @Test + void testIsolatedVertices() { + // 5 vertices, no edges — all isolated + List> adj = buildGraph(5, new int[][] {}); + List bridges = TarjanBridges.findBridges(5, adj); + assertTrue(bridges.isEmpty()); + } + + @Test + void testLargeCycleNoBridges() { + // Cycle: 0-1-2-3-4-5-6-7-0 + int n = 8; + int[][] edges = new int[n][2]; + for (int i = 0; i < n; i++) { + edges[i] = new int[] {i, (i + 1) % n}; + } + List> adj = buildGraph(n, edges); + List bridges = TarjanBridges.findBridges(n, adj); + assertTrue(bridges.isEmpty()); + } + + @Test + void testComplexGraphWithMultipleCyclesAndBridges() { + // Two cycles connected by a single bridge edge: + // Cycle A: 0-1-2-0 + // Cycle B: 3-4-5-3 + // Bridge: 2-3 + List> adj = buildGraph(6, new int[][] {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5}, {5, 3}, {2, 3}}); + List bridges = TarjanBridges.findBridges(6, adj); + assertEquals(1, bridges.size()); + assertEquals(2, bridges.get(0)[0]); + assertEquals(3, bridges.get(0)[1]); + } + + @Test + void testNegativeVertexCountThrowsException() { + assertThrows(IllegalArgumentException.class, () -> TarjanBridges.findBridges(-1, new ArrayList<>())); + } + + @Test + void testNullAdjacencyListThrowsException() { + assertThrows(IllegalArgumentException.class, () -> TarjanBridges.findBridges(3, null)); + } + + @Test + void testMismatchedAdjacencyListSizeThrowsException() { + List> adj = buildGraph(2, new int[][] {{0, 1}}); + assertThrows(IllegalArgumentException.class, () -> TarjanBridges.findBridges(5, adj)); + } + + @Test + void testStarGraphAllEdgesAreBridges() { + // Star graph: center vertex 0 connected to 1, 2, 3, 4 + List> adj = buildGraph(5, new int[][] {{0, 1}, {0, 2}, {0, 3}, {0, 4}}); + List bridges = TarjanBridges.findBridges(5, adj); + assertEquals(4, bridges.size()); + } + + @Test + void testBridgeBetweenTwoCycles() { + // Two squares connected by one bridge: + // Square 1: 0-1-2-3-0 + // Square 2: 4-5-6-7-4 + // Bridge: 3-4 + List> adj = buildGraph(8, new int[][] {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {3, 4}}); + List bridges = TarjanBridges.findBridges(8, adj); + assertEquals(1, bridges.size()); + assertEquals(3, bridges.get(0)[0]); + assertEquals(4, bridges.get(0)[1]); + } +} From 8be75436ed863ac7e198dc889a71669d11c30354 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 23:37:03 +0200 Subject: [PATCH 03/10] chore(deps): bump com.puppycrawl.tools:checkstyle from 13.3.0 to 13.4.0 (#7352) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 13.3.0 to 13.4.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-13.3.0...checkstyle-13.4.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dab7447430e5..c3629f165361 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ com.puppycrawl.tools checkstyle - 13.3.0 + 13.4.0 From 5753f46f538e460b1246da699bfa700a091cddc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:40:59 +0000 Subject: [PATCH 04/10] chore(deps-dev): bump com.github.spotbugs:spotbugs-maven-plugin from 4.9.8.2 to 4.9.8.3 (#7353) chore(deps-dev): bump com.github.spotbugs:spotbugs-maven-plugin Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.8.2 to 4.9.8.3. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.8.2...spotbugs-maven-plugin-4.9.8.3) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.8.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c3629f165361..74b6cd9bd485 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.8.2 + 4.9.8.3 spotbugs-exclude.xml true From bdbbecedfc2b5263a4ddd749be3bfe34670e8b7d Mon Sep 17 00:00:00 2001 From: Senrian <47714364+Senrian@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:03:01 +0800 Subject: [PATCH 05/10] Improve JumpSearch documentation with detailed explanation and example (#7354) Issue: #7349 Added: - Step-by-step algorithm explanation - Worked example with input/output - Time and space complexity analysis - Notes on when to use Jump Search vs Binary/Linear Search - @see references to related search algorithms --- .../thealgorithms/searches/JumpSearch.java | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/thealgorithms/searches/JumpSearch.java b/src/main/java/com/thealgorithms/searches/JumpSearch.java index 8dcec3a819a4..cbc494c8c16a 100644 --- a/src/main/java/com/thealgorithms/searches/JumpSearch.java +++ b/src/main/java/com/thealgorithms/searches/JumpSearch.java @@ -12,26 +12,50 @@ * Once the range is found, a linear search is performed within that block. * *

- * 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. + * How it works: + *

    + *
  1. Calculate the optimal block size as √n (square root of array length)
  2. + *
  3. Jump ahead by the block size until the current element is greater than the target
  4. + *
  5. Perform a linear search backwards within the identified block
  6. + *
* *

- * Worst-case performance: O(√N)
- * Best-case performance: O(1)
- * Average performance: O(√N)
- * Worst-case space complexity: O(1) + * Example:
+ * Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19], Target: 9
+ * Step 1: Jump from index 0 → 3 → 6 (9 < 13, so we found the block)
+ * Step 2: Linear search from index 3 to 6: found 9 at index 4
+ * Result: Index = 4 + * + *

+ * Time Complexity:
+ * - Best-case: O(1) - element found at first position
+ * - Average: O(√n) - optimal block size reduces jumps
+ * - Worst-case: O(√n) - element at end of array or not present
+ * + *

+ * Space Complexity: O(1) - only uses a constant amount of extra space + * + *

+ * Note: 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. * *

* 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 From d2744b56173ca4936765d55457190098501e4617 Mon Sep 17 00:00:00 2001 From: Suraj Devatha <42767118+surajdm123@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:56:16 -0700 Subject: [PATCH 06/10] Fixes #7350: Binary Search Reliability Improvement (#7351) --- .../searches/BinarySearchTest.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/test/java/com/thealgorithms/searches/BinarySearchTest.java b/src/test/java/com/thealgorithms/searches/BinarySearchTest.java index bd4620a7fa7d..00bed165734e 100644 --- a/src/test/java/com/thealgorithms/searches/BinarySearchTest.java +++ b/src/test/java/com/thealgorithms/searches/BinarySearchTest.java @@ -105,4 +105,90 @@ void testBinarySearchLargeArray() { int expectedIndex = 9999; // Index of the last element assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the last element should be 9999."); } + + /** + * Test for binary search with null array. + */ + @Test + void testBinarySearchNullArray() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = null; + int key = 5; // Key to search + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, binarySearch.find(array, key), "The element should not be found in a null array."); + } + + /** + * Test for binary search with duplicate elements. + */ + @Test + void testBinarySearchWithDuplicates() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1, 2, 2, 2, 3}; + int key = 2; // Element present multiple times + + int result = binarySearch.find(array, key); + assertEquals(2, array[result], "The returned index should contain the searched element."); + } + + /** + * Test for binary search where all elements are the same. + */ + @Test + void testBinarySearchAllElementsSame() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {5, 5, 5, 5, 5}; + int key = 5; // All elements match + + int result = binarySearch.find(array, key); + assertEquals(5, array[result], "The returned index should contain the searched element."); + } + + /** + * Test for binary search with negative numbers. + */ + @Test + void testBinarySearchNegativeNumbers() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {-10, -5, 0, 5, 10}; + int key = -5; // Element present + int expectedIndex = 1; // Index of the element + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the element should be 1."); + } + + /** + * Test for binary search when key is smaller than all elements. + */ + @Test + void testBinarySearchKeySmallerThanAll() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {10, 20, 30}; + int key = 5; // Smaller than all elements + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, binarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for binary search when key is larger than all elements. + */ + @Test + void testBinarySearchKeyLargerThanAll() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {10, 20, 30}; + int key = 40; // Larger than all elements + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, binarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for binary search with String array. + */ + @Test + void testBinarySearchStrings() { + BinarySearch binarySearch = new BinarySearch(); + String[] array = {"apple", "banana", "cherry", "date"}; + String key = "cherry"; // Element present + int expectedIndex = 2; // Index of the element + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the element should be 2."); + } } From ee343363c6158d91d7a180ac88e3749482bed291 Mon Sep 17 00:00:00 2001 From: kvadrik <41710943+kvadrik@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:34:30 +0300 Subject: [PATCH 07/10] Correlation function for discrete variable correlation (#7326) * Correlation function for discrete variable correlation * Add unit tests for Correlation class Added unit tests for the Correlation class to validate correlation calculations under various scenarios, including linear dependence and constant values. * Added missing bracket * Refactor variable initialization in correlation method * Remove unused imports and clean up CorrelationTest * Fix formatting of variable declarations in correlation method * Update Correlation.java * Fix formatting in CorrelationTest.java * Enhance comments in correlation function Added detailed comments to the correlation function for better understanding. * Add correlation tests for various scenarios * Format comments for clarity in correlation method * Fix formatting and comments in correlation method --- .../com/thealgorithms/maths/Correlation.java | 51 +++++++++++++++++++ .../thealgorithms/maths/CorrelationTest.java | 51 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/Correlation.java create mode 100644 src/test/java/com/thealgorithms/maths/CorrelationTest.java diff --git a/src/main/java/com/thealgorithms/maths/Correlation.java b/src/main/java/com/thealgorithms/maths/Correlation.java new file mode 100644 index 000000000000..a46445fb23b7 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Correlation.java @@ -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); + } + } +} diff --git a/src/test/java/com/thealgorithms/maths/CorrelationTest.java b/src/test/java/com/thealgorithms/maths/CorrelationTest.java new file mode 100644 index 000000000000..96867d56ad5e --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/CorrelationTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test class for Correlation class + */ +public class CorrelationTest { + + public static final double DELTA = 1e-9; + + // Regular correlation test + public void testCorrelationFirst() { + double[] x = {1, 2, 3, 4}; + double[] y = {7, 1, 4, 9}; + int n = 4; + assertEquals(0.3319700011, Correlation.correlation(x, y, n), DELTA); + } + + // Regular correlation test (zero correlation) + public void testCorrelationSecond() { + double[] x = {1, 2, 3, 4}; + double[] y = {5, 0, 9, 2}; + int n = 4; + assertEquals(0, Correlation.correlation(x, y, n), DELTA); + } + + // Correlation with a constant variable is taken to be zero + public void testCorrelationConstant() { + double[] x = {1, 2, 3}; + double[] y = {4, 4, 4}; + int n = 3; + assertEquals(0, Correlation.correlation(x, y, n), DELTA); + } + + // Linear dependence gives correlation 1 + public void testCorrelationLinearDependence() { + double[] x = {1, 2, 3, 4}; + double[] y = {6, 8, 10, 12}; + int n = 4; + assertEquals(1, Correlation.correlation(x, y, n), DELTA); + } + + // Inverse linear dependence gives correlation -1 + public void testCorrelationInverseLinearDependence() { + double[] x = {1, 2, 3, 4, 5}; + double[] y = {18, 15, 12, 9, 6}; + int n = 5; + assertEquals(-1, Correlation.correlation(x, y, n), DELTA); + } +} From 635d54a9de573a51786b18b151579206b58ae947 Mon Sep 17 00:00:00 2001 From: Senrian <47714364+Senrian@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:03:57 +0800 Subject: [PATCH 08/10] fix(BinarySearch): add null key check to prevent NPE (fix #7356) (#7357) fix(BinarySearch): add null key check to prevent NPE Issue #7356: Add null check for the search value to prevent potential NullPointerException. --- .../java/com/thealgorithms/searches/BinarySearch.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch.java b/src/main/java/com/thealgorithms/searches/BinarySearch.java index 7a5361b280ea..ca873fc6eafa 100644 --- a/src/main/java/com/thealgorithms/searches/BinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/BinarySearch.java @@ -58,11 +58,18 @@ class BinarySearch implements SearchAlgorithm { */ @Override public > 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); } From 9729e56fc7b54197460ee449d94d33284c68d7aa Mon Sep 17 00:00:00 2001 From: Vansh Sharma Date: Wed, 1 Apr 2026 17:25:29 +0530 Subject: [PATCH 09/10] Use BigInteger to prevent overflow in factorial calculation (#7358) * Use BigInteger to prevent overflow in factorial calculation * chore: remove unnecessary comment * update * Fix: improve factorial implementation and formatting * test: final fix for FactorialTest logic and formatting * chore: final formatting and test fix for BigInteger * chore: final formatting and test fix for BigInteger --- .../com/thealgorithms/maths/Factorial.java | 18 +++++++----------- .../com/thealgorithms/maths/FactorialTest.java | 9 +++++---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/Factorial.java b/src/main/java/com/thealgorithms/maths/Factorial.java index 511cc1f84f05..8ad219a3066c 100644 --- a/src/main/java/com/thealgorithms/maths/Factorial.java +++ b/src/main/java/com/thealgorithms/maths/Factorial.java @@ -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; } } diff --git a/src/test/java/com/thealgorithms/maths/FactorialTest.java b/src/test/java/com/thealgorithms/maths/FactorialTest.java index 3ff7097b8113..a393136696e1 100644 --- a/src/test/java/com/thealgorithms/maths/FactorialTest.java +++ b/src/test/java/com/thealgorithms/maths/FactorialTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.math.BigInteger; import org.junit.jupiter.api.Test; public class FactorialTest { @@ -16,9 +17,9 @@ public void testWhenInvalidInoutProvidedShouldThrowException() { @Test public void testCorrectFactorialCalculation() { - assertEquals(1, Factorial.factorial(0)); - assertEquals(1, Factorial.factorial(1)); - assertEquals(120, Factorial.factorial(5)); - assertEquals(3628800, Factorial.factorial(10)); + assertEquals(BigInteger.ONE, Factorial.factorial(0)); + assertEquals(BigInteger.ONE, Factorial.factorial(1)); + assertEquals(BigInteger.valueOf(120), Factorial.factorial(5)); + assertEquals(BigInteger.valueOf(3628800), Factorial.factorial(10)); } } From d44b608482c1c07abc59973c4b013b2a91488f17 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Sat, 4 Apr 2026 04:15:44 +0000 Subject: [PATCH 10/10] fix: prevent NPE when array contains null elements When searching for a non-null key in an array that contains null elements, the sentinel linear search would throw a NullPointerException because it called array[i].compareTo(key) without checking if array[i] is null. Added null check for array[i] in the while loop condition to prevent NPE and return the correct index when array elements themselves are null. Issue: #7318 (related) --- .../java/com/thealgorithms/searches/SentinelLinearSearch.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java b/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java index 1a5903a5d134..473fc2c3f094 100644 --- a/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java +++ b/src/main/java/com/thealgorithms/searches/SentinelLinearSearch.java @@ -65,7 +65,8 @@ public > 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++; }