> 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);
}
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:
+ *
+ * - Calculate the optimal block size as √n (square root of array length)
+ * - Jump ahead by the block size until the current element is greater than the target
+ * - Perform a linear search backwards within the identified block
+ *
*
*
- * 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
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;
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++;
}
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]);
+ }
+}
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);
+ }
+}
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));
}
}
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.");
+ }
}