result) {
+
+ if (current.length() == chars.length) {
+ result.add(current.toString());
+ return;
+ }
+
+ for (int i = 0; i < chars.length; i++) {
+
+ // skip duplicates
+ if (i > 0 && chars[i] == chars[i - 1] && !used[i - 1]) {
+ continue;
+ }
+
+ if (!used[i]) {
+ used[i] = true;
+ current.append(chars[i]);
+
+ backtrack(chars, used, current, result);
+
+ // undo changes
+ used[i] = false;
+ current.deleteCharAt(current.length() - 1);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thealgorithms/bitmanipulation/BitRotate.java b/src/main/java/com/thealgorithms/bitmanipulation/BitRotate.java
new file mode 100644
index 000000000000..226e09e78d1f
--- /dev/null
+++ b/src/main/java/com/thealgorithms/bitmanipulation/BitRotate.java
@@ -0,0 +1,83 @@
+package com.thealgorithms.bitmanipulation;
+
+/**
+ * Utility class for performing circular bit rotations on 32-bit integers.
+ * Bit rotation is a circular shift operation where bits shifted out on one end
+ * are reinserted on the opposite end.
+ *
+ * This class provides methods for both left and right circular rotations,
+ * supporting only 32-bit integer operations with proper shift normalization
+ * and error handling.
+ *
+ * @see Bit Rotation
+ */
+public final class BitRotate {
+
+ /**
+ * Private constructor to prevent instantiation.
+ * This is a utility class with only static methods.
+ */
+ private BitRotate() {
+ throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
+ }
+
+ /**
+ * Performs a circular left rotation (left shift) on a 32-bit integer.
+ * Bits shifted out from the left side are inserted on the right side.
+ *
+ * @param value the 32-bit integer value to rotate
+ * @param shift the number of positions to rotate left (must be non-negative)
+ * @return the result of left rotating the value by the specified shift amount
+ * @throws IllegalArgumentException if shift is negative
+ *
+ * @example
+ * // Binary: 10000000 00000000 00000000 00000001
+ * rotateLeft(0x80000001, 1)
+ * // Returns: 3 (binary: 00000000 00000000 00000000 00000011)
+ */
+ public static int rotateLeft(int value, int shift) {
+ if (shift < 0) {
+ throw new IllegalArgumentException("Shift amount cannot be negative: " + shift);
+ }
+
+ // Normalize shift to the range [0, 31] using modulo 32
+ shift = shift % 32;
+
+ if (shift == 0) {
+ return value;
+ }
+
+ // Left rotation: (value << shift) | (value >>> (32 - shift))
+ return (value << shift) | (value >>> (32 - shift));
+ }
+
+ /**
+ * Performs a circular right rotation (right shift) on a 32-bit integer.
+ * Bits shifted out from the right side are inserted on the left side.
+ *
+ * @param value the 32-bit integer value to rotate
+ * @param shift the number of positions to rotate right (must be non-negative)
+ * @return the result of right rotating the value by the specified shift amount
+ * @throws IllegalArgumentException if shift is negative
+ *
+ * @example
+ * // Binary: 00000000 00000000 00000000 00000011
+ * rotateRight(3, 1)
+ * // Returns: -2147483647 (binary: 10000000 00000000 00000000 00000001)
+ */
+ public static int rotateRight(int value, int shift) {
+ if (shift < 0) {
+ throw new IllegalArgumentException("Shift amount cannot be negative: " + shift);
+ }
+
+ // Normalize shift to the range [0, 31] using modulo 32
+ shift = shift % 32;
+
+ if (shift == 0) {
+ return value;
+ }
+
+ // Right rotation: (value >>> shift) | (value << (32 - shift))
+ return (value >>> shift) | (value << (32 - shift));
+ }
+}
diff --git a/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java b/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java
index 242f35fc35f2..7df522ca8f69 100644
--- a/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java
+++ b/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java
@@ -1,79 +1,79 @@
package com.thealgorithms.bitmanipulation;
-public class CountSetBits {
+/**
+ * Utility class to count total set bits from 1 to N
+ * A set bit is a bit in binary representation that is 1
+ *
+ * @author navadeep
+ */
+public final class CountSetBits {
+
+ private CountSetBits() {
+ // Utility class, prevent instantiation
+ }
/**
- * The below algorithm is called as Brian Kernighan's algorithm
- * We can use Brian Kernighanβs algorithm to improve the above naive algorithmβs performance.
- The idea is to only consider the set bits of an integer by turning off its rightmost set bit
- (after counting it), so the next iteration of the loop considers the next rightmost bit.
-
- The expression n & (n-1) can be used to turn off the rightmost set bit of a number n. This
- works as the expression n-1 flips all the bits after the rightmost set bit of n, including the
- rightmost set bit itself. Therefore, n & (n-1) results in the last bit flipped of n.
-
- For example, consider number 52, which is 00110100 in binary, and has a total 3 bits set.
-
- 1st iteration of the loop: n = 52
-
- 00110100 & (n)
- 00110011 (n-1)
- ~~~~~~~~
- 00110000
+ * Counts total number of set bits in all numbers from 1 to n
+ * Time Complexity: O(log n)
+ *
+ * @param n the upper limit (inclusive)
+ * @return total count of set bits from 1 to n
+ * @throws IllegalArgumentException if n is negative
+ */
+ public static int countSetBits(int n) {
+ if (n < 0) {
+ throw new IllegalArgumentException("Input must be non-negative");
+ }
+ if (n == 0) {
+ return 0;
+ }
- 2nd iteration of the loop: n = 48
+ // Find the largest power of 2 <= n
+ int x = largestPowerOf2InNumber(n);
- 00110000 & (n)
- 00101111 (n-1)
- ~~~~~~~~
- 00100000
+ // Total bits at position x: x * 2^(x-1)
+ int bitsAtPositionX = x * (1 << (x - 1));
+ // Remaining numbers after 2^x
+ int remainingNumbers = n - (1 << x) + 1;
- 3rd iteration of the loop: n = 32
+ // Recursively count for the rest
+ int rest = countSetBits(n - (1 << x));
- 00100000 & (n)
- 00011111 (n-1)
- ~~~~~~~~
- 00000000 (n = 0)
+ return bitsAtPositionX + remainingNumbers + rest;
+ }
- * @param num takes Long number whose number of set bit is to be found
- * @return the count of set bits in the binary equivalent
- */
- public long countSetBits(long num) {
- long cnt = 0;
- while (num > 0) {
- cnt++;
- num &= (num - 1);
+ /**
+ * Finds the position of the most significant bit in n
+ *
+ * @param n the number
+ * @return position of MSB (0-indexed from right)
+ */
+ private static int largestPowerOf2InNumber(int n) {
+ int position = 0;
+ while ((1 << position) <= n) {
+ position++;
}
- return cnt;
+ return position - 1;
}
/**
- * This approach takes O(1) running time to count the set bits, but requires a pre-processing.
+ * Alternative naive approach - counts set bits by iterating through all numbers
+ * Time Complexity: O(n log n)
*
- * So, we divide our 32-bit input into 8-bit chunks, with four chunks. We have 8 bits in each chunk.
- *
- * Then the range is from 0-255 (0 to 2^7).
- * So, we may need to count set bits from 0 to 255 in individual chunks.
- *
- * @param num takes a long number
- * @return the count of set bits in the binary equivalent
+ * @param n the upper limit (inclusive)
+ * @return total count of set bits from 1 to n
*/
- public int lookupApproach(int num) {
- int[] table = new int[256];
- table[0] = 0;
-
- for (int i = 1; i < 256; i++) {
- table[i] = (i & 1) + table[i >> 1]; // i >> 1 equals to i/2
+ public static int countSetBitsNaive(int n) {
+ if (n < 0) {
+ throw new IllegalArgumentException("Input must be non-negative");
}
- int res = 0;
- for (int i = 0; i < 4; i++) {
- res += table[num & 0xff];
- num >>= 8;
+ int count = 0;
+ for (int i = 1; i <= n; i++) {
+ count += Integer.bitCount(i);
}
-
- return res;
+ return count;
}
}
diff --git a/src/main/java/com/thealgorithms/ciphers/AES.java b/src/main/java/com/thealgorithms/ciphers/AES.java
index 1c283f6b7655..df51eba55310 100644
--- a/src/main/java/com/thealgorithms/ciphers/AES.java
+++ b/src/main/java/com/thealgorithms/ciphers/AES.java
@@ -2738,7 +2738,7 @@ public static BigInteger decrypt(BigInteger cipherText, BigInteger key) {
public static void main(String[] args) {
try (Scanner input = new Scanner(System.in)) {
- System.out.println("Enter (e) letter for encrpyt or (d) letter for decrypt :");
+ System.out.println("Enter (e) letter for encrypt or (d) letter for decrypt :");
char choice = input.nextLine().charAt(0);
String in;
switch (choice) {
diff --git a/src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java b/src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java
new file mode 100644
index 000000000000..7733f5cb46f2
--- /dev/null
+++ b/src/main/java/com/thealgorithms/ciphers/OneTimePadCipher.java
@@ -0,0 +1,89 @@
+package com.thealgorithms.ciphers;
+
+import java.security.SecureRandom;
+import java.util.Objects;
+
+/**
+ * One-Time Pad (OTP) cipher implementation.
+ *
+ * The One-Time Pad is information-theoretically secure if:
+ *
+ * - The key is truly random.
+ * - The key length is at least as long as the plaintext.
+ * - The key is used only once and kept secret.
+ *
+ *
+ * This implementation is for educational purposes only and should not be
+ * used in production systems.
+ */
+public final class OneTimePadCipher {
+
+ private static final SecureRandom RANDOM = new SecureRandom();
+
+ private OneTimePadCipher() {
+ // utility class
+ }
+
+ /**
+ * Generates a random key of the given length in bytes.
+ *
+ * @param length the length of the key in bytes, must be non-negative
+ * @return a new random key
+ * @throws IllegalArgumentException if length is negative
+ */
+ public static byte[] generateKey(int length) {
+ if (length < 0) {
+ throw new IllegalArgumentException("length must be non-negative");
+ }
+ byte[] key = new byte[length];
+ RANDOM.nextBytes(key);
+ return key;
+ }
+
+ /**
+ * Encrypts the given plaintext bytes using the provided key.
+ *
The key length must be exactly the same as the plaintext length.
+ *
+ * @param plaintext the plaintext bytes, must not be {@code null}
+ * @param key the one-time pad key bytes, must not be {@code null}
+ * @return the ciphertext bytes
+ * @throws IllegalArgumentException if the key length does not match plaintext length
+ * @throws NullPointerException if plaintext or key is {@code null}
+ */
+ public static byte[] encrypt(byte[] plaintext, byte[] key) {
+ validateInputs(plaintext, key);
+ return xor(plaintext, key);
+ }
+
+ /**
+ * Decrypts the given ciphertext bytes using the provided key.
+ *
For a One-Time Pad, decryption is identical to encryption:
+ * {@code plaintext = ciphertext XOR key}.
+ *
+ * @param ciphertext the ciphertext bytes, must not be {@code null}
+ * @param key the one-time pad key bytes, must not be {@code null}
+ * @return the decrypted plaintext bytes
+ * @throws IllegalArgumentException if the key length does not match ciphertext length
+ * @throws NullPointerException if ciphertext or key is {@code null}
+ */
+ public static byte[] decrypt(byte[] ciphertext, byte[] key) {
+ validateInputs(ciphertext, key);
+ return xor(ciphertext, key);
+ }
+
+ private static void validateInputs(byte[] input, byte[] key) {
+ Objects.requireNonNull(input, "input must not be null");
+ Objects.requireNonNull(key, "key must not be null");
+ if (input.length != key.length) {
+ throw new IllegalArgumentException("Key length must match input length");
+ }
+ }
+
+ private static byte[] xor(byte[] data, byte[] key) {
+ byte[] result = new byte[data.length];
+ for (int i = 0; i < data.length; i++) {
+ result[i] = (byte) (data[i] ^ key[i]);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/conversions/TemperatureConverter.java b/src/main/java/com/thealgorithms/conversions/TemperatureConverter.java
new file mode 100644
index 000000000000..901db17c665d
--- /dev/null
+++ b/src/main/java/com/thealgorithms/conversions/TemperatureConverter.java
@@ -0,0 +1,46 @@
+package com.thealgorithms.conversions;
+
+/**
+ * A utility class to convert between different temperature units.
+ *
+ *
This class supports conversions between the following units:
+ *
+ * - Celsius
+ * - Fahrenheit
+ * - Kelvin
+ *
+ *
+ * This class is final and cannot be instantiated.
+ *
+ * @author krishna-medapati (https://github.com/krishna-medapati)
+ * @see Wikipedia: Temperature Conversion
+ */
+public final class TemperatureConverter {
+
+ private TemperatureConverter() {
+ }
+
+ public static double celsiusToFahrenheit(double celsius) {
+ return celsius * 9.0 / 5.0 + 32.0;
+ }
+
+ public static double celsiusToKelvin(double celsius) {
+ return celsius + 273.15;
+ }
+
+ public static double fahrenheitToCelsius(double fahrenheit) {
+ return (fahrenheit - 32.0) * 5.0 / 9.0;
+ }
+
+ public static double fahrenheitToKelvin(double fahrenheit) {
+ return (fahrenheit - 32.0) * 5.0 / 9.0 + 273.15;
+ }
+
+ public static double kelvinToCelsius(double kelvin) {
+ return kelvin - 273.15;
+ }
+
+ public static double kelvinToFahrenheit(double kelvin) {
+ return (kelvin - 273.15) * 9.0 / 5.0 + 32.0;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java b/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java
index 30030de6c1bd..50726380621a 100644
--- a/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java
+++ b/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java
@@ -16,7 +16,7 @@ private TurkishToLatinConversion() {
* 2. Replace all turkish characters with their corresponding latin characters
* 3. Return the converted string
*
- * @param param String paramter
+ * @param param String parameter
* @return String
*/
public static String convertTurkishToLatin(String param) {
diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java b/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java
index 47c5f0d0b98e..5184dae58d28 100644
--- a/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java
+++ b/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java
@@ -160,7 +160,7 @@ public void show(int source, int end,
break;
}
}
- if (neg == 0) { // Go ahead and show results of computaion
+ if (neg == 0) { // Go ahead and show results of computation
System.out.println("Distance is: " + dist[end]);
System.out.println("Path followed:");
System.out.print(source + " ");
diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java b/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java
index c1d47df457da..a54b0b75e4dc 100644
--- a/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java
+++ b/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java
@@ -141,7 +141,7 @@ private int[][] adjacency() {
*
* @param from the parent vertex to check for adjacency
* @param to the child vertex to check for adjacency
- * @return whether or not the vertices are adjancent
+ * @return whether or not the vertices are adjacent
*/
private boolean adjacencyOfEdgeDoesExist(int from, int to) {
return (this.adjacency()[from][to] != AdjacencyMatrixGraph.EDGE_NONE);
@@ -162,7 +162,7 @@ public boolean vertexDoesExist(int aVertex) {
*
* @param from the parent vertex to check for adjacency
* @param to the child vertex to check for adjacency
- * @return whether or not the vertices are adjancent
+ * @return whether or not the vertices are adjacent
*/
public boolean edgeDoesExist(int from, int to) {
if (this.vertexDoesExist(from) && this.vertexDoesExist(to)) {
diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java
index 7a263fc08ac5..834de9c77881 100644
--- a/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java
+++ b/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java
@@ -387,7 +387,7 @@ public class HeapNode {
private HeapNode parent;
/*
- * a constructor for a heapNode withe key @param (key)
+ * a constructor for a heapNode with key @param (key)
* prev == next == this
* parent == child == null
*/
diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java b/src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java
new file mode 100644
index 000000000000..ad7229760fd0
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueue.java
@@ -0,0 +1,327 @@
+package com.thealgorithms.datastructures.heaps;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * An addressable (indexed) min-priority queue with O(log n) updates.
+ *
+ *
Key features:
+ *
+ * - Each element E is tracked by a handle (its current heap index) via a map,
+ * enabling O(log n) {@code remove(e)} and O(log n) key updates
+ * ({@code changeKey/decreaseKey/increaseKey}).
+ * - The queue order is determined by the provided {@link Comparator}. If the
+ * comparator is {@code null}, elements must implement {@link Comparable}
+ * (same contract as {@link java.util.PriorityQueue}).
+ * - By default this implementation uses {@link IdentityHashMap} for the index
+ * mapping to avoid issues with duplicate-equals elements or mutable equals/hashCode.
+ * If you need value-based equality, switch to {@code HashMap} and read the caveats
+ * in the class-level Javadoc carefully.
+ *
+ *
+ * IMPORTANT contracts
+ *
+ * - Do not mutate comparator-relevant fields of an element directly while it is
+ * inside the queue. Always use {@code changeKey}/{@code decreaseKey}/{@code increaseKey}
+ * so the heap can be restored accordingly.
+ * - If you replace {@link IdentityHashMap} with {@link HashMap}, you must ensure:
+ * (a) no two distinct elements are {@code equals()}-equal at the same time in the queue, and
+ * (b) {@code equals/hashCode} of elements remain stable while enqueued.
+ * - {@code peek()} returns {@code null} when empty (matching {@link java.util.PriorityQueue}).
+ * - Not thread-safe.
+ *
+ *
+ * Complexities:
+ * {@code offer, poll, remove(e), changeKey, decreaseKey, increaseKey} are O(log n);
+ * {@code peek, isEmpty, size, contains} are O(1).
+ */
+public class IndexedPriorityQueue {
+
+ /** Binary heap storage (min-heap). */
+ private Object[] heap;
+
+ /** Number of elements in the heap. */
+ private int size;
+
+ /** Comparator used for ordering; if null, elements must be Comparable. */
+ private final Comparator super E> cmp;
+
+ /**
+ * Index map: element -> current heap index.
+ * We use IdentityHashMap by default to:
+ *
+ * - allow duplicate-equals elements;
+ * - avoid corruption when equals/hashCode are mutable or not ID-based.
+ *
+ * If you prefer value-based semantics, replace with HashMap and
+ * respect the warnings in the class Javadoc.
+ */
+ private final IdentityHashMap index;
+
+ private static final int DEFAULT_INITIAL_CAPACITY = 11;
+
+ public IndexedPriorityQueue() {
+ this(DEFAULT_INITIAL_CAPACITY, null);
+ }
+
+ public IndexedPriorityQueue(Comparator super E> cmp) {
+ this(DEFAULT_INITIAL_CAPACITY, cmp);
+ }
+
+ public IndexedPriorityQueue(int initialCapacity, Comparator super E> cmp) {
+ if (initialCapacity < 1) {
+ throw new IllegalArgumentException("initialCapacity < 1");
+ }
+ this.heap = new Object[initialCapacity];
+ this.cmp = cmp;
+ this.index = new IdentityHashMap<>();
+ }
+
+ /** Returns current number of elements. */
+ public int size() {
+ return size;
+ }
+
+ /** Returns {@code true} if empty. */
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Returns the minimum element without removing it, or {@code null} if empty.
+ * Matches {@link java.util.PriorityQueue#peek()} behavior.
+ */
+ @SuppressWarnings("unchecked")
+ public E peek() {
+ return size == 0 ? null : (E) heap[0];
+ }
+
+ /**
+ * Inserts the specified element (O(log n)).
+ * @throws NullPointerException if {@code e} is null
+ * @throws ClassCastException if {@code cmp == null} and {@code e} is not Comparable,
+ * or if incompatible with other elements
+ */
+ public boolean offer(E e) {
+ Objects.requireNonNull(e, "element is null");
+ if (size >= heap.length) {
+ grow(size + 1);
+ }
+ // Insert at the end and bubble up. siftUp will maintain 'index' for all touched nodes.
+ siftUp(size, e);
+ size++;
+ return true;
+ }
+
+ /**
+ * Removes and returns the minimum element (O(log n)), or {@code null} if empty.
+ */
+ @SuppressWarnings("unchecked")
+ public E poll() {
+ if (size == 0) {
+ return null;
+ }
+ E min = (E) heap[0];
+ removeAt(0); // updates map and heap structure
+ return min;
+ }
+
+ /**
+ * Removes one occurrence of the specified element e (O(log n)) if present.
+ * Uses the index map for O(1) lookup.
+ */
+ public boolean remove(Object o) {
+ Integer i = index.get(o);
+ if (i == null) {
+ return false;
+ }
+ removeAt(i);
+ return true;
+ }
+
+ /** O(1): returns whether the queue currently contains the given element reference. */
+ public boolean contains(Object o) {
+ return index.containsKey(o);
+ }
+
+ /** Clears the heap and the index map. */
+ public void clear() {
+ Arrays.fill(heap, 0, size, null);
+ index.clear();
+ size = 0;
+ }
+
+ // ------------------------------------------------------------------------------------
+ // Key update API
+ // ------------------------------------------------------------------------------------
+
+ /**
+ * Changes comparator-relevant fields of {@code e} via the provided {@code mutator},
+ * then restores the heap in O(log n) by bubbling in the correct direction.
+ *
+ * IMPORTANT: The mutator must not change {@code equals/hashCode} of {@code e}
+ * if you migrate this implementation to value-based indexing (HashMap).
+ *
+ * @throws IllegalArgumentException if {@code e} is not in the queue
+ */
+ public void changeKey(E e, Consumer mutator) {
+ Integer i = index.get(e);
+ if (i == null) {
+ throw new IllegalArgumentException("Element not in queue");
+ }
+ // Mutate fields used by comparator (do NOT mutate equality/hash if using value-based map)
+ mutator.accept(e);
+ // Try bubbling up; if no movement occurred, bubble down.
+ if (!siftUp(i)) {
+ siftDown(i);
+ }
+ }
+
+ /**
+ * Faster variant if the new key is strictly smaller (higher priority).
+ * Performs a single sift-up (O(log n)).
+ */
+ public void decreaseKey(E e, Consumer mutator) {
+ Integer i = index.get(e);
+ if (i == null) {
+ throw new IllegalArgumentException("Element not in queue");
+ }
+ mutator.accept(e);
+ siftUp(i);
+ }
+
+ /**
+ * Faster variant if the new key is strictly larger (lower priority).
+ * Performs a single sift-down (O(log n)).
+ */
+ public void increaseKey(E e, Consumer mutator) {
+ Integer i = index.get(e);
+ if (i == null) {
+ throw new IllegalArgumentException("Element not in queue");
+ }
+ mutator.accept(e);
+ siftDown(i);
+ }
+
+ // ------------------------------------------------------------------------------------
+ // Internal utilities
+ // ------------------------------------------------------------------------------------
+
+ /** Grows the internal array to accommodate at least {@code minCapacity}. */
+ private void grow(int minCapacity) {
+ int old = heap.length;
+ int pref = (old < 64) ? old + 2 : old + (old >> 1); // +2 if small, else +50%
+ int newCap = Math.max(minCapacity, pref);
+ heap = Arrays.copyOf(heap, newCap);
+ }
+
+ @SuppressWarnings("unchecked")
+ private int compare(E a, E b) {
+ if (cmp != null) {
+ return cmp.compare(a, b);
+ }
+ return ((Comparable super E>) a).compareTo(b);
+ }
+
+ /**
+ * Inserts item {@code x} at position {@code k}, bubbling up while maintaining the heap.
+ * Also maintains the index map for all moved elements.
+ */
+ @SuppressWarnings("unchecked")
+ private void siftUp(int k, E x) {
+ while (k > 0) {
+ int p = (k - 1) >>> 1;
+ E e = (E) heap[p];
+ if (compare(x, e) >= 0) {
+ break;
+ }
+ heap[k] = e;
+ index.put(e, k);
+ k = p;
+ }
+ heap[k] = x;
+ index.put(x, k);
+ }
+
+ /**
+ * Attempts to bubble up the element currently at {@code k}.
+ * @return true if it moved; false otherwise.
+ */
+ @SuppressWarnings("unchecked")
+ private boolean siftUp(int k) {
+ int orig = k;
+ E x = (E) heap[k];
+ while (k > 0) {
+ int p = (k - 1) >>> 1;
+ E e = (E) heap[p];
+ if (compare(x, e) >= 0) {
+ break;
+ }
+ heap[k] = e;
+ index.put(e, k);
+ k = p;
+ }
+ if (k != orig) {
+ heap[k] = x;
+ index.put(x, k);
+ return true;
+ }
+ return false;
+ }
+
+ /** Bubbles down the element currently at {@code k}. */
+ @SuppressWarnings("unchecked")
+ private void siftDown(int k) {
+ int n = size;
+ E x = (E) heap[k];
+ int half = n >>> 1; // loop while k has at least one child
+ while (k < half) {
+ int child = (k << 1) + 1; // assume left is smaller
+ E c = (E) heap[child];
+ int r = child + 1;
+ if (r < n && compare(c, (E) heap[r]) > 0) {
+ child = r;
+ c = (E) heap[child];
+ }
+ if (compare(x, c) <= 0) {
+ break;
+ }
+ heap[k] = c;
+ index.put(c, k);
+ k = child;
+ }
+ heap[k] = x;
+ index.put(x, k);
+ }
+
+ /**
+ * Removes the element at heap index {@code i}, restoring the heap afterwards.
+ * Returns nothing; the standard {@code PriorityQueue} returns a displaced
+ * element in a rare case to help its iterator. We don't need that here, so
+ * we keep the API simple.
+ */
+ @SuppressWarnings("unchecked")
+ private void removeAt(int i) {
+ int n = --size; // last index after removal
+ E moved = (E) heap[n];
+ E removed = (E) heap[i];
+ heap[n] = null; // help GC
+ index.remove(removed); // drop mapping for removed element
+
+ if (i == n) {
+ return; // removed last element; done
+ }
+
+ heap[i] = moved;
+ index.put(moved, i);
+
+ // Try sift-up first (cheap if key decreased); if no movement, sift-down.
+ if (!siftUp(i)) {
+ siftDown(i);
+ }
+ }
+}
diff --git a/src/main/java/com/thealgorithms/datastructures/lists/README.md b/src/main/java/com/thealgorithms/datastructures/lists/README.md
index 5a19c3bfa990..5a0a43b923e1 100644
--- a/src/main/java/com/thealgorithms/datastructures/lists/README.md
+++ b/src/main/java/com/thealgorithms/datastructures/lists/README.md
@@ -28,6 +28,6 @@ The `next` variable points to the next node in the data structure and value stor
4. `CreateAndDetectLoop.java` : Create and detect a loop in a linked list.
5. `DoublyLinkedList.java` : A modification of singly linked list which has a `prev` pointer to point to the previous node.
6. `MergeKSortedLinkedlist.java` : Merges K sorted linked list with mergesort (mergesort is also the most efficient sorting algorithm for linked list).
-7. `RandomNode.java` : Selects a random node from given linked list and diplays it.
+7. `RandomNode.java` : Selects a random node from given linked list and displays it.
8. `SkipList.java` : Data Structure used for storing a sorted list of elements with help of a Linked list hierarchy that connects to subsequences of elements.
9. `TortoiseHareAlgo.java` : Finds the middle element of a linked list using the fast and slow pointer (Tortoise-Hare) algorithm.
diff --git a/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java b/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java
index a5ca48670f2c..b877a5843b98 100644
--- a/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java
+++ b/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java
@@ -9,7 +9,7 @@
* give numbers that are bigger, a higher priority. Queues in theory have no
* fixed size but when using an array implementation it does.
*
- * Additional contibutions made by: PuneetTri(https://github.com/PuneetTri)
+ * Additional contributions made by: PuneetTri(https://github.com/PuneetTri)
*/
class PriorityQueue {
@@ -32,8 +32,8 @@ class PriorityQueue {
PriorityQueue() {
/* If capacity is not defined, default size of 11 would be used
- * capacity=max+1 because we cant access 0th element of PQ, and to
- * accomodate (max)th elements we need capacity to be max+1.
+ * capacity=max+1 because we can't access 0th element of PQ, and to
+ * accommodate (max)th elements we need capacity to be max+1.
* Parent is at position k, child at position (k*2,k*2+1), if we
* use position 0 in our queue, its child would be at:
* (0*2, 0*2+1) -> (0,0). This is why we start at position 1
@@ -127,7 +127,7 @@ public int remove() {
if (isEmpty()) {
throw new RuntimeException("Queue is Empty");
} else {
- int max = queueArray[1]; // By defintion of our max-heap, value at queueArray[1] pos is
+ int max = queueArray[1]; // By definition of our max-heap, value at queueArray[1] pos is
// the greatest
// Swap max and last element
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java
index e0309122cc12..07fc5c87b6c4 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java
@@ -1,7 +1,7 @@
package com.thealgorithms.datastructures.trees;
/*
-* Avl is algo that balance itself while adding new alues to tree
+* Avl is algo that balance itself while adding new values to tree
* by rotating branches of binary tree and make itself Binary seaarch tree
* there are four cases which has to tackle
* rotating - left right ,left left,right right,right left
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java b/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java
index 0245372fe012..2c94224ddeb4 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java
@@ -30,7 +30,7 @@ public BSTRecursiveGeneric() {
}
/**
- * Displays the tree is a structed format
+ * Displays the tree is a structured format
*/
public void prettyDisplay() {
prettyDisplay(root, 0);
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java b/src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java
new file mode 100644
index 000000000000..2f9b3b489d56
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/trees/BinaryTreeToString.java
@@ -0,0 +1,100 @@
+package com.thealgorithms.datastructures.trees;
+
+/**
+ * Leetcode 606: Construct String from Binary Tree:
+ * https://leetcode.com/problems/construct-string-from-binary-tree/
+ *
+ * Utility class to convert a {@link BinaryTree} into its string representation.
+ *
+ * The conversion follows a preorder traversal pattern (root β left β right)
+ * and uses parentheses to denote the tree structure.
+ * Empty parentheses "()" are used to explicitly represent missing left children
+ * when a right child exists, ensuring the structure is unambiguous.
+ *
+ *
+ * Rules:
+ *
+ * - Each node is represented as {@code (value)}.
+ * - If a node has only a right child, include {@code ()} before the right
+ * child
+ * to indicate the missing left child.
+ * - If a node has no children, it appears as just {@code (value)}.
+ * - The outermost parentheses are removed from the final string.
+ *
+ *
+ * Example:
+ *
+ *
+ * Input tree:
+ * 1
+ * / \
+ * 2 3
+ * \
+ * 4
+ *
+ * Output string:
+ * "1(2()(4))(3)"
+ *
+ *
+ *
+ * This implementation matches the logic from LeetCode problem 606:
+ * Construct String from Binary Tree.
+ *
+ *
+ * @author Muhammad Junaid
+ * @see BinaryTree
+ */
+public class BinaryTreeToString {
+
+ /** String builder used to accumulate the string representation. */
+ private StringBuilder sb;
+
+ /**
+ * Converts a binary tree (given its root node) to its string representation.
+ *
+ * @param root the root node of the binary tree
+ * @return the string representation of the binary tree, or an empty string if
+ * the tree is null
+ */
+ public String tree2str(BinaryTree.Node root) {
+ if (root == null) {
+ return "";
+ }
+
+ sb = new StringBuilder();
+ dfs(root);
+
+ // Remove the leading and trailing parentheses added by the root call
+ return sb.substring(1, sb.length() - 1);
+ }
+
+ /**
+ * Performs a recursive depth-first traversal to build the string.
+ * Each recursive call appends the node value and its children (if any)
+ * enclosed in parentheses.
+ *
+ * @param node the current node being processed
+ */
+ private void dfs(BinaryTree.Node node) {
+ if (node == null) {
+ return;
+ }
+
+ sb.append("(").append(node.data);
+
+ // Recursively build left and right subtrees
+ if (node.left != null) {
+ dfs(node.left);
+ }
+
+ // Handle the special case: right child exists but left child is null
+ if (node.right != null && node.left == null) {
+ sb.append("()");
+ dfs(node.right);
+ } else if (node.right != null) {
+ dfs(node.right);
+ }
+
+ sb.append(")");
+ }
+}
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java b/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java
index 214e111b9f1a..fff84111663b 100644
--- a/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java
+++ b/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java
@@ -21,7 +21,7 @@
*
* Solution 1: Brute Force Solution: Do an inorder traversal and save result
* into an array. Iterate over the array to get an element equal to or greater
- * than current key. Time Complexity: O(n) Space Complexity: O(n) for auxillary
+ * than current key. Time Complexity: O(n) Space Complexity: O(n) for auxiliary
* array to save inorder representation of tree.
*
*
@@ -29,7 +29,7 @@
* into an array.Since array is sorted do a binary search over the array to get
* an element equal to or greater than current key. Time Complexity: O(n) for
* traversal of tree and O(lg(n)) for binary search in array. Total = O(n) Space
- * Complexity: O(n) for auxillary array to save inorder representation of tree.
+ * Complexity: O(n) for auxiliary array to save inorder representation of tree.
*
*
* Solution 3: Optimal We can do a DFS search on given tree in following
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java b/src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java
new file mode 100644
index 000000000000..0b29dd6f5f5e
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/trees/CentroidDecomposition.java
@@ -0,0 +1,217 @@
+package com.thealgorithms.datastructures.trees;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Centroid Decomposition is a divide-and-conquer technique for trees.
+ * It recursively partitions a tree by finding centroids - nodes whose removal
+ * creates balanced subtrees (each with at most N/2 nodes).
+ *
+ *
+ * Time Complexity: O(N log N) for construction
+ * Space Complexity: O(N)
+ *
+ *
+ * Applications:
+ * - Distance queries on trees
+ * - Path counting problems
+ * - Nearest neighbor searches
+ *
+ * @see Centroid Decomposition
+ * @see Centroid Decomposition Tutorial
+ * @author lens161
+ */
+public final class CentroidDecomposition {
+
+ private CentroidDecomposition() {
+ }
+
+ /**
+ * Represents the centroid tree structure.
+ */
+ public static final class CentroidTree {
+ private final int n;
+ private final List> adj;
+ private final int[] parent;
+ private final int[] subtreeSize;
+ private final boolean[] removed;
+ private int root;
+
+ /**
+ * Constructs a centroid tree from an adjacency list.
+ *
+ * @param adj adjacency list representation of the tree (0-indexed)
+ * @throws IllegalArgumentException if tree is empty or null
+ */
+ public CentroidTree(List> adj) {
+ if (adj == null || adj.isEmpty()) {
+ throw new IllegalArgumentException("Tree cannot be empty or null");
+ }
+
+ this.n = adj.size();
+ this.adj = adj;
+ this.parent = new int[n];
+ this.subtreeSize = new int[n];
+ this.removed = new boolean[n];
+ Arrays.fill(parent, -1);
+
+ // Build centroid tree starting from node 0
+ this.root = decompose(0, -1);
+ }
+
+ /**
+ * Recursively builds the centroid tree.
+ *
+ * @param u current node
+ * @param p parent in centroid tree
+ * @return centroid of current component
+ */
+ private int decompose(int u, int p) {
+ int size = getSubtreeSize(u, -1);
+ int centroid = findCentroid(u, -1, size);
+
+ removed[centroid] = true;
+ parent[centroid] = p;
+
+ // Recursively decompose each subtree
+ for (int v : adj.get(centroid)) {
+ if (!removed[v]) {
+ decompose(v, centroid);
+ }
+ }
+
+ return centroid;
+ }
+
+ /**
+ * Calculates subtree size from node u.
+ *
+ * @param u current node
+ * @param p parent node (-1 for root)
+ * @return size of subtree rooted at u
+ */
+ private int getSubtreeSize(int u, int p) {
+ subtreeSize[u] = 1;
+ for (int v : adj.get(u)) {
+ if (v != p && !removed[v]) {
+ subtreeSize[u] += getSubtreeSize(v, u);
+ }
+ }
+ return subtreeSize[u];
+ }
+
+ /**
+ * Finds the centroid of a subtree.
+ * A centroid is a node whose removal creates components with size <= totalSize/2.
+ *
+ * @param u current node
+ * @param p parent node
+ * @param totalSize total size of current component
+ * @return centroid node
+ */
+ private int findCentroid(int u, int p, int totalSize) {
+ for (int v : adj.get(u)) {
+ if (v != p && !removed[v] && subtreeSize[v] > totalSize / 2) {
+ return findCentroid(v, u, totalSize);
+ }
+ }
+ return u;
+ }
+
+ /**
+ * Gets the parent of a node in the centroid tree.
+ *
+ * @param node the node
+ * @return parent node in centroid tree, or -1 if root
+ */
+ public int getParent(int node) {
+ if (node < 0 || node >= n) {
+ throw new IllegalArgumentException("Invalid node: " + node);
+ }
+ return parent[node];
+ }
+
+ /**
+ * Gets the root of the centroid tree.
+ *
+ * @return root node
+ */
+ public int getRoot() {
+ return root;
+ }
+
+ /**
+ * Gets the number of nodes in the tree.
+ *
+ * @return number of nodes
+ */
+ public int size() {
+ return n;
+ }
+
+ /**
+ * Returns the centroid tree structure as a string.
+ * Format: node -> parent (or ROOT for root node)
+ *
+ * @return string representation
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Centroid Tree:\n");
+ for (int i = 0; i < n; i++) {
+ sb.append("Node ").append(i).append(" -> ");
+ if (parent[i] == -1) {
+ sb.append("ROOT");
+ } else {
+ sb.append("Parent ").append(parent[i]);
+ }
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Creates a centroid tree from an edge list.
+ *
+ * @param n number of nodes (0-indexed: 0 to n-1)
+ * @param edges list of edges where each edge is [u, v]
+ * @return CentroidTree object
+ * @throws IllegalArgumentException if n <= 0 or edges is invalid
+ */
+ public static CentroidTree buildFromEdges(int n, int[][] edges) {
+ if (n <= 0) {
+ throw new IllegalArgumentException("Number of nodes must be positive");
+ }
+ if (edges == null) {
+ throw new IllegalArgumentException("Edges cannot be null");
+ }
+ if (edges.length != n - 1) {
+ throw new IllegalArgumentException("Tree must have exactly n-1 edges");
+ }
+
+ List> adj = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ adj.add(new ArrayList<>());
+ }
+
+ for (int[] edge : edges) {
+ if (edge.length != 2) {
+ throw new IllegalArgumentException("Each edge must have exactly 2 nodes");
+ }
+ int u = edge[0];
+ int v = edge[1];
+
+ if (u < 0 || u >= n || v < 0 || v >= n) {
+ throw new IllegalArgumentException("Invalid node in edge: [" + u + ", " + v + "]");
+ }
+
+ adj.get(u).add(v);
+ adj.get(v).add(u);
+ }
+
+ return new CentroidTree(adj);
+ }
+}
diff --git a/src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java b/src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java
new file mode 100644
index 000000000000..fd8876cecb70
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTree.java
@@ -0,0 +1,145 @@
+/*
+ * TheAlgorithms (https://github.com/TheAlgorithms/Java)
+ * Author: Shewale41
+ * This file is licensed under the MIT License.
+ */
+
+package com.thealgorithms.datastructures.trees;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Threaded binary tree implementation that supports insertion and
+ * in-order traversal without recursion or stack by using threads.
+ *
+ * In this implementation, a node's null left/right pointers are used
+ * to point to the in-order predecessor/successor respectively. Two flags
+ * indicate whether left/right pointers are real children or threads.
+ *
+ * @see Wikipedia:
+ * Threaded binary tree
+ */
+public final class ThreadedBinaryTree {
+
+ private Node root;
+
+ private static final class Node {
+ int value;
+ Node left;
+ Node right;
+ boolean leftIsThread;
+ boolean rightIsThread;
+
+ Node(int value) {
+ this.value = value;
+ this.left = null;
+ this.right = null;
+ this.leftIsThread = false;
+ this.rightIsThread = false;
+ }
+ }
+
+ public ThreadedBinaryTree() {
+ this.root = null;
+ }
+
+ /**
+ * Inserts a value into the threaded binary tree. Duplicate values are inserted
+ * to the right subtree (consistent deterministic rule).
+ *
+ * @param value the integer value to insert
+ */
+ public void insert(int value) {
+ Node newNode = new Node(value);
+ if (root == null) {
+ root = newNode;
+ return;
+ }
+
+ Node current = root;
+ Node parent = null;
+
+ while (true) {
+ parent = current;
+ if (value < current.value) {
+ if (!current.leftIsThread && current.left != null) {
+ current = current.left;
+ } else {
+ break;
+ }
+ } else { // value >= current.value
+ if (!current.rightIsThread && current.right != null) {
+ current = current.right;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (value < parent.value) {
+ // attach newNode as left child
+ newNode.left = parent.left;
+ newNode.leftIsThread = parent.leftIsThread;
+ newNode.right = parent;
+ newNode.rightIsThread = true;
+
+ parent.left = newNode;
+ parent.leftIsThread = false;
+ } else {
+ // attach newNode as right child
+ newNode.right = parent.right;
+ newNode.rightIsThread = parent.rightIsThread;
+ newNode.left = parent;
+ newNode.leftIsThread = true;
+
+ parent.right = newNode;
+ parent.rightIsThread = false;
+ }
+ }
+
+ /**
+ * Returns the in-order traversal of the tree as a list of integers.
+ * Traversal is done without recursion or an explicit stack by following threads.
+ *
+ * @return list containing the in-order sequence of node values
+ */
+ public List inorderTraversal() {
+ List result = new ArrayList<>();
+ Node current = root;
+ if (current == null) {
+ return result;
+ }
+
+ // Move to the leftmost node
+ while (current.left != null && !current.leftIsThread) {
+ current = current.left;
+ }
+
+ while (current != null) {
+ result.add(current.value);
+
+ // If right pointer is a thread, follow it
+ if (current.rightIsThread) {
+ current = current.right;
+ } else {
+ // Move to leftmost node in right subtree
+ current = current.right;
+ while (current != null && !current.leftIsThread && current.left != null) {
+ current = current.left;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Helper: checks whether the tree is empty.
+ *
+ * @return true if tree has no nodes
+ */
+ public boolean isEmpty() {
+ return root == null;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java b/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java
index 95d53ecb1f7a..d69288f72200 100644
--- a/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java
+++ b/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java
@@ -3,7 +3,7 @@
import java.util.Collection;
/**
- * {@link TreeNode} extension that holds a {@link Collection} of refrences to
+ * {@link TreeNode} extension that holds a {@link Collection} of references to
* child Nodes.
*
* @param The type of the data held in the Node.
@@ -18,7 +18,7 @@ public class LargeTreeNode extends TreeNode {
private Collection> childNodes;
/**
- * Empty contructor.
+ * Empty constructor.
*/
public LargeTreeNode() {
super();
diff --git a/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java b/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java
index 769ffc2a9a96..bf3e795dd74b 100644
--- a/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java
+++ b/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java
@@ -15,7 +15,7 @@ public class SimpleNode extends Node {
private SimpleNode nextNode;
/**
- * Empty contructor.
+ * Empty constructor.
*/
public SimpleNode() {
super();
diff --git a/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java b/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java
index 215f01a6ef59..eefffacee8d6 100644
--- a/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java
+++ b/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java
@@ -11,16 +11,16 @@
public class SimpleTreeNode extends TreeNode {
/**
- * Refrence to the child Node on the left.
+ * Reference to the child Node on the left.
*/
private SimpleTreeNode leftNode;
/**
- * Refrence to the child Node on the right.
+ * Reference to the child Node on the right.
*/
private SimpleTreeNode rightNode;
/**
- * Empty contructor.
+ * Empty constructor.
*/
public SimpleTreeNode() {
super();
diff --git a/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java b/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java
index 13c9212306c1..1639bec6e5a0 100644
--- a/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java
+++ b/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java
@@ -12,7 +12,7 @@
public abstract class TreeNode extends Node {
/**
- * Refernce to the parent Node.
+ * Reference to the parent Node.
*/
private TreeNode parentNode;
/**
@@ -21,7 +21,7 @@ public abstract class TreeNode extends Node {
private int depth;
/**
- * Empty contructor.
+ * Empty constructor.
*/
public TreeNode() {
super();
diff --git a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java
index cd26f9213651..4c9c40c83174 100644
--- a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java
+++ b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java
@@ -182,7 +182,7 @@ public double closestPair(final Location[] a, final int indexNum) {
double minLeftArea; // Minimum length of left array
double minRightArea; // Minimum length of right array
- double minValue; // Minimum lengt
+ double minValue; // Minimum length
minLeftArea = closestPair(leftArray, divideX); // recursive closestPair
minRightArea = closestPair(rightArray, indexNum - divideX);
diff --git a/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java b/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java
index 610b1b78a36a..0e8d9442138c 100644
--- a/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java
+++ b/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java
@@ -161,7 +161,7 @@ public int getY() {
* function dominates the argument point.
*
* @param p1 the point that is compared
- * @return true if the point wich calls the function dominates p1 false
+ * @return true if the point which calls the function dominates p1 false
* otherwise.
*/
public boolean dominates(Point p1) {
diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java b/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java
index 8494492f293f..ccd54ee4349a 100644
--- a/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java
+++ b/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java
@@ -12,8 +12,8 @@ private BoundaryFill() {
* Get the color at the given co-odrinates of a 2D image
*
* @param image The image to be filled
- * @param xCoordinate The x co-ordinate of which color is to be obtained
- * @param yCoordinate The y co-ordinate of which color is to be obtained
+ * @param xCoordinate The x coordinate of which color is to be obtained
+ * @param yCoordinate The y coordinate of which color is to be obtained
*/
public static int getPixel(int[][] image, int xCoordinate, int yCoordinate) {
return image[xCoordinate][yCoordinate];
@@ -23,8 +23,8 @@ public static int getPixel(int[][] image, int xCoordinate, int yCoordinate) {
* Put the color at the given co-odrinates of a 2D image
*
* @param image The image to be filed
- * @param xCoordinate The x co-ordinate at which color is to be filled
- * @param yCoordinate The y co-ordinate at which color is to be filled
+ * @param xCoordinate The x coordinate at which color is to be filled
+ * @param yCoordinate The y coordinate at which color is to be filled
*/
public static void putPixel(int[][] image, int xCoordinate, int yCoordinate, int newColor) {
image[xCoordinate][yCoordinate] = newColor;
@@ -34,8 +34,8 @@ public static void putPixel(int[][] image, int xCoordinate, int yCoordinate, int
* Fill the 2D image with new color
*
* @param image The image to be filed
- * @param xCoordinate The x co-ordinate at which color is to be filled
- * @param yCoordinate The y co-ordinate at which color is to be filled
+ * @param xCoordinate The x coordinate at which color is to be filled
+ * @param yCoordinate The y coordinate at which color is to be filled
* @param newColor The new color which to be filled in the image
* @param boundaryColor The old color which is to be replaced in the image
*/
diff --git a/src/main/java/com/thealgorithms/graph/GomoryHuTree.java b/src/main/java/com/thealgorithms/graph/GomoryHuTree.java
new file mode 100644
index 000000000000..f8c110f25571
--- /dev/null
+++ b/src/main/java/com/thealgorithms/graph/GomoryHuTree.java
@@ -0,0 +1,144 @@
+package com.thealgorithms.graph;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Queue;
+
+/**
+ * GomoryβHu tree construction for undirected graphs via nβ1 max-flow computations.
+ *
+ * API: {@code buildTree(int[][])} returns {@code {parent, weight}} arrays for the tree.
+ *
+ * @see Wikipedia: GomoryβHu tree
+ */
+
+public final class GomoryHuTree {
+ private GomoryHuTree() {
+ }
+
+ public static int[][] buildTree(int[][] cap) {
+ validateCapacityMatrix(cap);
+ final int n = cap.length;
+ if (n == 1) {
+ return new int[][] {new int[] {-1}, new int[] {0}};
+ }
+
+ int[] parent = new int[n];
+ int[] weight = new int[n];
+ Arrays.fill(parent, 0);
+ parent[0] = -1;
+ weight[0] = 0;
+
+ for (int s = 1; s < n; s++) {
+ int t = parent[s];
+ MaxFlowResult res = edmondsKarpWithMinCut(cap, s, t);
+ int f = res.flow;
+ weight[s] = f;
+
+ for (int v = 0; v < n; v++) {
+ if (v != s && parent[v] == t && res.reachable[v]) {
+ parent[v] = s;
+ }
+ }
+
+ if (t != 0 && res.reachable[parent[t]]) {
+ parent[s] = parent[t];
+ parent[t] = s;
+ weight[s] = weight[t];
+ weight[t] = f;
+ }
+ }
+ return new int[][] {parent, weight};
+ }
+
+ private static void validateCapacityMatrix(int[][] cap) {
+ if (cap == null || cap.length == 0) {
+ throw new IllegalArgumentException("Capacity matrix must not be null or empty");
+ }
+ final int n = cap.length;
+ for (int i = 0; i < n; i++) {
+ if (cap[i] == null || cap[i].length != n) {
+ throw new IllegalArgumentException("Capacity matrix must be square");
+ }
+ for (int j = 0; j < n; j++) {
+ if (cap[i][j] < 0) {
+ throw new IllegalArgumentException("Capacities must be non-negative");
+ }
+ }
+ }
+ }
+
+ private static final class MaxFlowResult {
+ final int flow;
+ final boolean[] reachable;
+ MaxFlowResult(int flow, boolean[] reachable) {
+ this.flow = flow;
+ this.reachable = reachable;
+ }
+ }
+
+ private static MaxFlowResult edmondsKarpWithMinCut(int[][] capacity, int source, int sink) {
+ final int n = capacity.length;
+ int[][] residual = new int[n][n];
+ for (int i = 0; i < n; i++) {
+ residual[i] = Arrays.copyOf(capacity[i], n);
+ }
+
+ int[] parent = new int[n];
+ int maxFlow = 0;
+
+ while (bfs(residual, source, sink, parent)) {
+ int pathFlow = Integer.MAX_VALUE;
+ for (int v = sink; v != source; v = parent[v]) {
+ int u = parent[v];
+ pathFlow = Math.min(pathFlow, residual[u][v]);
+ }
+ for (int v = sink; v != source; v = parent[v]) {
+ int u = parent[v];
+ residual[u][v] -= pathFlow;
+ residual[v][u] += pathFlow;
+ }
+ maxFlow += pathFlow;
+ }
+
+ boolean[] reachable = new boolean[n];
+ markReachable(residual, source, reachable);
+ return new MaxFlowResult(maxFlow, reachable);
+ }
+
+ private static boolean bfs(int[][] residual, int source, int sink, int[] parent) {
+ Arrays.fill(parent, -1);
+ parent[source] = source;
+ Queue q = new ArrayDeque<>();
+ q.add(source);
+ while (!q.isEmpty()) {
+ int u = q.poll();
+ for (int v = 0; v < residual.length; v++) {
+ if (residual[u][v] > 0 && parent[v] == -1) {
+ parent[v] = u;
+ if (v == sink) {
+ return true;
+ }
+ q.add(v);
+ }
+ }
+ }
+ return false;
+ }
+
+ private static void markReachable(int[][] residual, int source, boolean[] vis) {
+ Arrays.fill(vis, false);
+ Queue q = new ArrayDeque<>();
+ vis[source] = true;
+ q.add(source);
+ while (!q.isEmpty()) {
+ int u = q.poll();
+ for (int v = 0; v < residual.length; v++) {
+ if (!vis[v] && residual[u][v] > 0) {
+ vis[v] = true;
+ q.add(v);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/AbundantNumber.java b/src/main/java/com/thealgorithms/maths/AbundantNumber.java
new file mode 100644
index 000000000000..804ac4d71477
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/AbundantNumber.java
@@ -0,0 +1,58 @@
+package com.thealgorithms.maths;
+
+/**
+ * In number theory, an abundant number or excessive number is a positive integer for which
+ * the sum of its proper divisors is greater than the number.
+ * Equivalently, it is a number for which the sum of proper divisors (or aliquot sum) is greater than n.
+ *
+ * The integer 12 is the first abundant number. Its proper divisors are 1, 2, 3, 4 and 6 for a total of 16.
+ *
+ * Wiki: https://en.wikipedia.org/wiki/Abundant_number
+ */
+public final class AbundantNumber {
+
+ private AbundantNumber() {
+ }
+
+ // Function to calculate sum of all divisors including n
+ private static int sumOfDivisors(int n) {
+ int sum = 1 + n; // 1 and n are always divisors
+ for (int i = 2; i <= n / 2; i++) {
+ if (n % i == 0) {
+ sum += i; // adding divisor to sum
+ }
+ }
+ return sum;
+ }
+
+ // Common validation method
+ private static void validatePositiveNumber(int number) {
+ if (number <= 0) {
+ throw new IllegalArgumentException("Number must be positive.");
+ }
+ }
+
+ /**
+ * Check if {@code number} is an Abundant number or not by checking sum of divisors > 2n
+ *
+ * @param number the number
+ * @return {@code true} if {@code number} is an Abundant number, otherwise false
+ */
+ public static boolean isAbundant(int number) {
+ validatePositiveNumber(number);
+
+ return sumOfDivisors(number) > 2 * number;
+ }
+
+ /**
+ * Check if {@code number} is an Abundant number or not by checking Aliquot Sum > n
+ *
+ * @param number the number
+ * @return {@code true} if {@code number} is a Abundant number, otherwise false
+ */
+ public static boolean isAbundantNumber(int number) {
+ validatePositiveNumber(number);
+
+ return AliquotSum.getAliquotSum(number) > number;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/Area.java b/src/main/java/com/thealgorithms/maths/Area.java
index a34ad6b01ab5..1eba6666dde3 100644
--- a/src/main/java/com/thealgorithms/maths/Area.java
+++ b/src/main/java/com/thealgorithms/maths/Area.java
@@ -96,7 +96,7 @@ public static double surfaceAreaCylinder(final double radius, final double heigh
throw new IllegalArgumentException(POSITIVE_RADIUS);
}
if (height <= 0) {
- throw new IllegalArgumentException(POSITIVE_RADIUS);
+ throw new IllegalArgumentException(POSITIVE_HEIGHT);
}
return 2 * (Math.PI * radius * radius + Math.PI * radius * height);
}
diff --git a/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java b/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java
index faec049b08a7..1de39adbc18a 100644
--- a/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java
+++ b/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java
@@ -1,8 +1,8 @@
package com.thealgorithms.maths;
/*
- * Java program for Binomial Cofficients
- * Binomial Cofficients: A binomial cofficient C(n,k) gives number ways
+ * Java program for Binomial Coefficients
+ * Binomial Coefficients: A binomial coefficient C(n,k) gives number ways
* in which k objects can be chosen from n objects.
* Wikipedia: https://en.wikipedia.org/wiki/Binomial_coefficient
*
diff --git a/src/main/java/com/thealgorithms/maths/EvilNumber.java b/src/main/java/com/thealgorithms/maths/EvilNumber.java
new file mode 100644
index 000000000000..419133702fd4
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/EvilNumber.java
@@ -0,0 +1,39 @@
+package com.thealgorithms.maths;
+
+/**
+ * In number theory, an evil number is a non-negative integer that has an even number of 1s in its binary expansion.
+ * Non-negative integers that are not evil are called odious numbers.
+ *
+ * Evil Number Wiki: https://en.wikipedia.org/wiki/Evil_number
+ * Odious Number Wiki: https://en.wikipedia.org/wiki/Odious_number
+ */
+public final class EvilNumber {
+
+ private EvilNumber() {
+ }
+
+ // Function to count number of one bits in a number using bitwise operators
+ private static int countOneBits(int number) {
+ int oneBitCounter = 0;
+ while (number > 0) {
+ oneBitCounter += number & 1; // increment count if last bit is 1
+ number >>= 1; // right shift to next bit
+ }
+ return oneBitCounter;
+ }
+
+ /**
+ * Check either {@code number} is an Evil number or Odious number
+ *
+ * @param number the number
+ * @return {@code true} if {@code number} is an Evil number, otherwise false (in case of of Odious number)
+ */
+ public static boolean isEvilNumber(int number) {
+ if (number < 0) {
+ throw new IllegalArgumentException("Negative numbers are not allowed.");
+ }
+
+ int noOfOneBits = countOneBits(number);
+ return noOfOneBits % 2 == 0;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/LuckyNumber.java b/src/main/java/com/thealgorithms/maths/LuckyNumber.java
new file mode 100644
index 000000000000..70308e1e0edd
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/LuckyNumber.java
@@ -0,0 +1,78 @@
+package com.thealgorithms.maths;
+
+/**
+ * In number theory, a lucky number is a natural number in a set which is generated by a certain "sieve".
+ * This sieve is similar to the sieve of Eratosthenes that generates the primes,
+ * but it eliminates numbers based on their position in the remaining set,
+ * instead of their value (or position in the initial set of natural numbers).
+ *
+ * Wiki: https://en.wikipedia.org/wiki/Lucky_number
+ */
+public final class LuckyNumber {
+
+ private LuckyNumber() {
+ }
+
+ // Common validation method
+ private static void validatePositiveNumber(int number) {
+ if (number <= 0) {
+ throw new IllegalArgumentException("Number must be positive.");
+ }
+ }
+
+ // Function to check recursively for Lucky Number
+ private static boolean isLuckyRecursiveApproach(int n, int counter) {
+ // Base case: If counter exceeds n, number is lucky
+ if (counter > n) {
+ return true;
+ }
+
+ // If number is eliminated in this step, it's not lucky
+ if (n % counter == 0) {
+ return false;
+ }
+
+ // Calculate new position after removing every counter-th number
+ int newNumber = n - (n / counter);
+
+ // Recursive call for next round
+ return isLuckyRecursiveApproach(newNumber, counter + 1);
+ }
+
+ /**
+ * Check if {@code number} is a Lucky number or not using recursive approach
+ *
+ * @param number the number
+ * @return {@code true} if {@code number} is a Lucky number, otherwise false
+ */
+ public static boolean isLuckyNumber(int number) {
+ validatePositiveNumber(number);
+ int counterStarting = 2;
+ return isLuckyRecursiveApproach(number, counterStarting);
+ }
+
+ /**
+ * Check if {@code number} is a Lucky number or not using iterative approach
+ *
+ * @param number the number
+ * @return {@code true} if {@code number} is a Lucky number, otherwise false
+ */
+ public static boolean isLucky(int number) {
+ validatePositiveNumber(number);
+
+ int counter = 2; // Position starts from 2 (since first elimination happens at 2)
+ int position = number; // The position of the number in the sequence
+
+ while (counter <= position) {
+ if (position % counter == 0) {
+ return false;
+ } // Number is eliminated
+
+ // Update the position of n after removing every counter-th number
+ position = position - (position / counter);
+ counter++;
+ }
+
+ return true; // Survives all eliminations β Lucky Number
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/Perimeter.java b/src/main/java/com/thealgorithms/maths/Perimeter.java
index f8aa1876d388..670851eb346b 100644
--- a/src/main/java/com/thealgorithms/maths/Perimeter.java
+++ b/src/main/java/com/thealgorithms/maths/Perimeter.java
@@ -27,7 +27,7 @@ public static float perimeterRegularPolygon(int n, float side) {
* @param side2 for length of side 2
* @param side3 for length of side 3
* @param sides for length of remaining sides
- * @return Perimeter of given trapezoid.
+ * @return Perimeter of given irregular polygon.
*/
public static float perimeterIrregularPolygon(float side1, float side2, float side3, float... sides) {
float perimeter = side1 + side2 + side3;
diff --git a/src/main/java/com/thealgorithms/maths/PowerOfFour.java b/src/main/java/com/thealgorithms/maths/PowerOfFour.java
new file mode 100644
index 000000000000..e5fe6255821b
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/PowerOfFour.java
@@ -0,0 +1,36 @@
+package com.thealgorithms.maths;
+
+/**
+ * Utility class for checking if a number is a power of four.
+ * A power of four is a number that can be expressed as 4^n where n is a non-negative integer.
+ * This class provides a method to determine if a given integer is a power of four using bit manipulation.
+ *
+ * @author krishna-medapati (https://github.com/krishna-medapati)
+ */
+public final class PowerOfFour {
+ private PowerOfFour() {
+ }
+
+ /**
+ * Checks if the given integer is a power of four.
+ *
+ * A number is considered a power of four if:
+ * 1. It is greater than zero
+ * 2. It has exactly one '1' bit in its binary representation (power of two)
+ * 3. The '1' bit is at an even position (0, 2, 4, 6, ...)
+ *
+ * The method uses the mask 0x55555555 (binary: 01010101010101010101010101010101)
+ * to check if the set bit is at an even position.
+ *
+ * @param number the integer to check
+ * @return true if the number is a power of four, false otherwise
+ */
+ public static boolean isPowerOfFour(int number) {
+ if (number <= 0) {
+ return false;
+ }
+ boolean isPowerOfTwo = (number & (number - 1)) == 0;
+ boolean hasEvenBitPosition = (number & 0x55555555) != 0;
+ return isPowerOfTwo && hasEvenBitPosition;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java b/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java
index ee59d0784ec4..780dd81dac7c 100644
--- a/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java
+++ b/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java
@@ -14,7 +14,7 @@
public final class SieveOfAtkin {
private SieveOfAtkin() {
- // Utlity class; prevent instantiation
+ // Utility class; prevent instantiation
}
/**
diff --git a/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java b/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java
index f22d22e8c6af..5a15c4201a15 100644
--- a/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java
+++ b/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java
@@ -1,66 +1,82 @@
package com.thealgorithms.maths;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
/**
- * @brief utility class implementing Sieve of Eratosthenes
+ * Sieve of Eratosthenes Algorithm
+ * An efficient algorithm to find all prime numbers up to a given limit.
+ *
+ * Algorithm:
+ * 1. Create a boolean array of size n+1, initially all true
+ * 2. Mark 0 and 1 as not prime
+ * 3. For each number i from 2 to sqrt(n):
+ * - If i is still marked as prime
+ * - Mark all multiples of i (starting from iΒ²) as not prime
+ * 4. Collect all numbers still marked as prime
+ *
+ * Time Complexity: O(n log log n)
+ * Space Complexity: O(n)
+ *
+ * @author Navadeep0007
+ * @see Sieve of Eratosthenes
*/
public final class SieveOfEratosthenes {
+
private SieveOfEratosthenes() {
+ // Utility class, prevent instantiation
}
- private static void checkInput(int n) {
- if (n <= 0) {
- throw new IllegalArgumentException("n must be positive.");
+ /**
+ * Finds all prime numbers up to n using the Sieve of Eratosthenes algorithm
+ *
+ * @param n the upper limit (inclusive)
+ * @return a list of all prime numbers from 2 to n
+ * @throws IllegalArgumentException if n is negative
+ */
+ public static List findPrimes(int n) {
+ if (n < 0) {
+ throw new IllegalArgumentException("Input must be non-negative");
}
- }
- private static Type[] sievePrimesTill(int n) {
- checkInput(n);
- Type[] isPrimeArray = new Type[n + 1];
- Arrays.fill(isPrimeArray, Type.PRIME);
- isPrimeArray[0] = Type.NOT_PRIME;
- isPrimeArray[1] = Type.NOT_PRIME;
+ if (n < 2) {
+ return new ArrayList<>();
+ }
+
+ // Create boolean array, initially all true
+ boolean[] isPrime = new boolean[n + 1];
+ for (int i = 2; i <= n; i++) {
+ isPrime[i] = true;
+ }
- double cap = Math.sqrt(n);
- for (int i = 2; i <= cap; i++) {
- if (isPrimeArray[i] == Type.PRIME) {
- for (int j = 2; i * j <= n; j++) {
- isPrimeArray[i * j] = Type.NOT_PRIME;
+ // Sieve process
+ for (int i = 2; i * i <= n; i++) {
+ if (isPrime[i]) {
+ // Mark all multiples of i as not prime
+ for (int j = i * i; j <= n; j += i) {
+ isPrime[j] = false;
}
}
}
- return isPrimeArray;
- }
-
- private static int countPrimes(Type[] isPrimeArray) {
- return (int) Arrays.stream(isPrimeArray).filter(element -> element == Type.PRIME).count();
- }
- private static int[] extractPrimes(Type[] isPrimeArray) {
- int numberOfPrimes = countPrimes(isPrimeArray);
- int[] primes = new int[numberOfPrimes];
- int primeIndex = 0;
- for (int curNumber = 0; curNumber < isPrimeArray.length; ++curNumber) {
- if (isPrimeArray[curNumber] == Type.PRIME) {
- primes[primeIndex++] = curNumber;
+ // Collect all prime numbers
+ List primes = new ArrayList<>();
+ for (int i = 2; i <= n; i++) {
+ if (isPrime[i]) {
+ primes.add(i);
}
}
+
return primes;
}
/**
- * @brief finds all of the prime numbers up to the given upper (inclusive) limit
- * @param n upper (inclusive) limit
- * @exception IllegalArgumentException n is non-positive
- * @return the array of all primes up to the given number (inclusive)
+ * Counts the number of prime numbers up to n
+ *
+ * @param n the upper limit (inclusive)
+ * @return count of prime numbers from 2 to n
*/
- public static int[] findPrimesTill(int n) {
- return extractPrimes(sievePrimesTill(n));
- }
-
- private enum Type {
- PRIME,
- NOT_PRIME,
+ public static int countPrimes(int n) {
+ return findPrimes(n).size();
}
}
diff --git a/src/main/java/com/thealgorithms/maths/SmithNumber.java b/src/main/java/com/thealgorithms/maths/SmithNumber.java
new file mode 100644
index 000000000000..c06e0023d9bb
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/SmithNumber.java
@@ -0,0 +1,52 @@
+package com.thealgorithms.maths;
+
+import com.thealgorithms.maths.Prime.PrimeCheck;
+
+/**
+ * In number theory, a smith number is a composite number for which, in a given number base,
+ * the sum of its digits is equal to the sum of the digits in its prime factorization in the same base.
+ *
+ * For example, in base 10, 378 = 21 X 33 X 71 is a Smith number since 3 + 7 + 8 = 2βXβ1 + 3βXβ3 + 7βXβ1,
+ * and 22 = 21 X 111 is a Smith number, because 2 + 2 = 2βXβ1 + (1 + 1)βXβ1.
+ *
+ * Wiki: https://en.wikipedia.org/wiki/Smith_number
+ */
+public final class SmithNumber {
+
+ private SmithNumber() {
+ }
+
+ private static int primeFactorDigitSum(int n) {
+ int sum = 0;
+ int num = n;
+
+ // Factorize the number using trial division
+ for (int i = 2; i * i <= num; i++) {
+ while (n % i == 0) { // while i divides n
+ sum += SumOfDigits.sumOfDigits(i); // add sum of digits of factor
+ n /= i; // divide n by the factor
+ }
+ }
+
+ // If n is still > 1, it itself is a prime factor
+ if (n > 1) {
+ sum += SumOfDigits.sumOfDigits(n);
+ }
+
+ return sum;
+ }
+
+ /**
+ * Check if {@code number} is Smith number or not
+ *
+ * @param number the number
+ * @return {@code true} if {@code number} is a Smith number, otherwise false
+ */
+ public static boolean isSmithNumber(int number) {
+ if (PrimeCheck.isPrime(number)) {
+ return false; // Smith numbers must be composite
+ }
+
+ return SumOfDigits.sumOfDigits(number) == primeFactorDigitSum(number);
+ }
+}
diff --git a/src/main/java/com/thealgorithms/matrix/LUDecomposition.java b/src/main/java/com/thealgorithms/matrix/LUDecomposition.java
new file mode 100644
index 000000000000..e41aaa201338
--- /dev/null
+++ b/src/main/java/com/thealgorithms/matrix/LUDecomposition.java
@@ -0,0 +1,88 @@
+package com.thealgorithms.matrix;
+
+/**
+ * LU Decomposition algorithm
+ * --------------------------
+ * Decomposes a square matrix a into a product of two matrices:
+ * a = l * u
+ * where:
+ * - l is a lower triangular matrix with 1s on its diagonal
+ * - u is an upper triangular matrix
+ *
+ * Reference:
+ * https://en.wikipedia.org/wiki/lu_decomposition
+ */
+public final class LUDecomposition {
+
+ private LUDecomposition() {
+ }
+
+ /**
+ * A helper class to store both l and u matrices
+ */
+ public static class LU {
+ double[][] l;
+ double[][] u;
+
+ LU(double[][] l, double[][] u) {
+ this.l = l;
+ this.u = u;
+ }
+ }
+
+ /**
+ * Performs LU Decomposition on a square matrix a
+ *
+ * @param a input square matrix
+ * @return LU object containing l and u matrices
+ */
+ public static LU decompose(double[][] a) {
+ int n = a.length;
+ double[][] l = new double[n][n];
+ double[][] u = new double[n][n];
+
+ for (int i = 0; i < n; i++) {
+ // upper triangular matrix
+ for (int k = i; k < n; k++) {
+ double sum = 0;
+ for (int j = 0; j < i; j++) {
+ sum += l[i][j] * u[j][k];
+ }
+ u[i][k] = a[i][k] - sum;
+ }
+
+ // lower triangular matrix
+ for (int k = i; k < n; k++) {
+ if (i == k) {
+ l[i][i] = 1; // diagonal as 1
+ } else {
+ double sum = 0;
+ for (int j = 0; j < i; j++) {
+ sum += l[k][j] * u[j][i];
+ }
+ l[k][i] = (a[k][i] - sum) / u[i][i];
+ }
+ }
+ }
+
+ return new LU(l, u);
+ }
+
+ /**
+ * Utility function to print a matrix
+ *
+ * @param m matrix to print
+ */
+ public static void printMatrix(double[][] m) {
+ for (double[] row : m) {
+ System.out.print("[");
+ for (int j = 0; j < row.length; j++) {
+ System.out.printf("%7.3f", row[j]);
+ if (j < row.length - 1) {
+ System.out.print(", ");
+ }
+ }
+ System.out.println("]");
+ }
+ }
+}
diff --git a/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java b/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java
index 9a7f255282ac..7ee8a1a9f0ed 100644
--- a/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java
+++ b/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java
@@ -40,7 +40,7 @@ static void printMatrix(int[][] arr) {
}
/**
- * Class containing the algo to roate matrix by 90 degree
+ * Class containing the algo to rotate matrix by 90 degree
*/
final class Rotate {
private Rotate() {
diff --git a/src/main/java/com/thealgorithms/others/BankersAlgorithm.java b/src/main/java/com/thealgorithms/others/BankersAlgorithm.java
index 836526529374..5abf633a1c12 100644
--- a/src/main/java/com/thealgorithms/others/BankersAlgorithm.java
+++ b/src/main/java/com/thealgorithms/others/BankersAlgorithm.java
@@ -3,7 +3,7 @@
import java.util.Scanner;
/**
- * This file contains an implementation of BANKER'S ALGORITM Wikipedia:
+ * This file contains an implementation of BANKER'S ALGORITHM Wikipedia:
* https://en.wikipedia.org/wiki/Banker%27s_algorithm
*
* The algorithm for finding out whether or not a system is in a safe state can
diff --git a/src/main/java/com/thealgorithms/others/CRCAlgorithm.java b/src/main/java/com/thealgorithms/others/CRCAlgorithm.java
index 00ddc86be820..2d0be15e0a7b 100644
--- a/src/main/java/com/thealgorithms/others/CRCAlgorithm.java
+++ b/src/main/java/com/thealgorithms/others/CRCAlgorithm.java
@@ -95,7 +95,7 @@ public int getCorrectMess() {
/**
* Resets some of the object's values, used on the main function, so that it
- * can be re-used, in order not to waste too much memory and time, by
+ * can be reused, in order not to waste too much memory and time, by
* creating new objects.
*/
public void refactor() {
@@ -171,7 +171,7 @@ public void divideMessageWithP(boolean check) {
* Once the message is transmitted, some of it's elements, is possible to
* change from 1 to 0, or from 0 to 1, because of the Bit Error Rate (ber).
* For every element of the message, a random double number is created. If
- * that number is smaller than ber, then the spesific element changes. On
+ * that number is smaller than ber, then the specific element changes. On
* the other hand, if it's bigger than ber, it does not. Based on these
* changes. the boolean variable messageChanged, gets the value: true, or
* false.
diff --git a/src/main/java/com/thealgorithms/others/GaussLegendre.java b/src/main/java/com/thealgorithms/others/GaussLegendre.java
index b56b51f158fb..acf76ae3b192 100644
--- a/src/main/java/com/thealgorithms/others/GaussLegendre.java
+++ b/src/main/java/com/thealgorithms/others/GaussLegendre.java
@@ -1,7 +1,7 @@
package com.thealgorithms.others;
/**
- * Guass Legendre Algorithm ref
+ * Gauss Legendre Algorithm ref
* https://en.wikipedia.org/wiki/GaussβLegendre_algorithm
*
* @author AKS1996
diff --git a/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java b/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java
index bb88c7e3ae2f..7a1a7aadd805 100644
--- a/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java
+++ b/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java
@@ -97,7 +97,7 @@ static void suggestionsRec(TrieNode root, String currPrefix) {
}
}
- // Fucntion to print suggestions for
+ // Function to print suggestions for
// given query prefix.
static int printAutoSuggestions(TrieNode root, final String query) {
TrieNode pCrawl = root;
diff --git a/src/main/java/com/thealgorithms/others/IterativeFloodFill.java b/src/main/java/com/thealgorithms/others/IterativeFloodFill.java
index 3ef90369bd52..3f685f418a3d 100644
--- a/src/main/java/com/thealgorithms/others/IterativeFloodFill.java
+++ b/src/main/java/com/thealgorithms/others/IterativeFloodFill.java
@@ -32,8 +32,8 @@ private IterativeFloodFill() {
* Iteratively fill the 2D image with new color
*
* @param image The image to be filled
- * @param x The x co-ordinate at which color is to be filled
- * @param y The y co-ordinate at which color is to be filled
+ * @param x The x coordinate at which color is to be filled
+ * @param y The y coordinate at which color is to be filled
* @param newColor The new color which to be filled in the image
* @param oldColor The old color which is to be replaced in the image
* @see FloodFill BFS
@@ -86,8 +86,8 @@ private static class Point {
* Checks if a pixel should be skipped during flood fill operation.
*
* @param image The image to get boundaries
- * @param x The x co-ordinate of pixel to check
- * @param y The y co-ordinate of pixel to check
+ * @param x The x coordinate of pixel to check
+ * @param y The y coordinate of pixel to check
* @param oldColor The old color which is to be replaced in the image
* @return {@code true} if pixel should be skipped, else {@code false}
*/
diff --git a/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java b/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java
index 0924b8569942..40a5f6a7a767 100644
--- a/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java
+++ b/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java
@@ -16,7 +16,7 @@ public abstract class MemoryManagementAlgorithms {
* blocks available.
* @param sizeOfProcesses: an int array that contains the sizes of the
* processes we need memory blocks for.
- * @return the ArrayList filled with Integers repressenting the memory
+ * @return the ArrayList filled with Integers representing the memory
* allocation that took place.
*/
public abstract ArrayList fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses);
@@ -91,7 +91,7 @@ private static int findBestFit(int[] blockSizes, int processSize) {
* blocks available.
* @param sizeOfProcesses: an int array that contains the sizes of the
* processes we need memory blocks for.
- * @return the ArrayList filled with Integers repressenting the memory
+ * @return the ArrayList filled with Integers representing the memory
* allocation that took place.
*/
public ArrayList fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) {
@@ -149,7 +149,7 @@ private static int findWorstFit(int[] blockSizes, int processSize) {
* blocks available.
* @param sizeOfProcesses: an int array that contains the sizes of the
* processes we need memory blocks for.
- * @return the ArrayList filled with Integers repressenting the memory
+ * @return the ArrayList filled with Integers representing the memory
* allocation that took place.
*/
public ArrayList fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) {
@@ -201,7 +201,7 @@ private static int findFirstFit(int[] blockSizes, int processSize) {
* blocks available.
* @param sizeOfProcesses: an int array that contains the sizes of the
* processes we need memory blocks for.
- * @return the ArrayList filled with Integers repressenting the memory
+ * @return the ArrayList filled with Integers representing the memory
* allocation that took place.
*/
public ArrayList fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) {
@@ -262,7 +262,7 @@ private int findNextFit(int[] blockSizes, int processSize) {
* blocks available.
* @param sizeOfProcesses: an int array that contains the sizes of the
* processes we need memory blocks for.
- * @return the ArrayList filled with Integers repressenting the memory
+ * @return the ArrayList filled with Integers representing the memory
* allocation that took place.
*/
public ArrayList fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) {
diff --git a/src/main/java/com/thealgorithms/physics/Kinematics.java b/src/main/java/com/thealgorithms/physics/Kinematics.java
new file mode 100644
index 000000000000..d017fe787afd
--- /dev/null
+++ b/src/main/java/com/thealgorithms/physics/Kinematics.java
@@ -0,0 +1,69 @@
+package com.thealgorithms.physics;
+/**
+ * Implements the fundamental "SUVAT" equations for motion
+ * under constant acceleration.
+ *
+ * @author [Priyanshu Kumar Singh](https://github.com/Priyanshu1303d)
+ * @see Wikipedia
+ */
+public final class Kinematics {
+ private Kinematics() {
+ }
+
+ /**
+ * Calculates the final velocity (v) of an object.
+ * Formula: v = u + at
+ *
+ * @param u Initial velocity (m/s).
+ * @param a Constant acceleration (m/s^2).
+ * @param t Time elapsed (s).
+ * @return The final velocity (m/s).
+ */
+
+ public static double calculateFinalVelocity(double u, double a, double t) {
+ return u + a * t;
+ }
+
+ /**
+ * Calculates the displacement (s) of an object.
+ * Formula: s = ut + 0.5 * a * t^2
+ *
+ * @param u Initial velocity (m/s).
+ * @param a Constant acceleration (m/s^2).
+ * @param t Time elapsed (s).
+ * @return The displacement (m).
+ */
+
+ public static double calculateDisplacement(double u, double a, double t) {
+ return u * t + 0.5 * a * t * t;
+ }
+
+ /**
+ * Calculates the displacement (s) of an object.
+ * Formula: v^2 = u^2 + 2 * a * s
+ *
+ * @param u Initial velocity (m/s).
+ * @param a Constant acceleration (m/s^2).
+ * @param s Displacement (m).
+ * @return The final velocity squared (m/s)^2.
+ */
+
+ public static double calculateFinalVelocitySquared(double u, double a, double s) {
+ return u * u + 2 * a * s;
+ }
+
+ /**
+ * Calculates the displacement (s) using the average velocity.
+ * Formula: s = (u + v) / 2 * t
+ *
+ * @param u Initial velocity (m/s).
+ * @param v Final velocity (m/s).
+ * @param t Time elapsed (s).
+ * @return The displacement (m).
+ */
+
+ public static double calculateDisplacementFromVelocities(double u, double v, double t) {
+ double velocitySum = u + v;
+ return velocitySum / 2 * t;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/physics/SnellLaw.java b/src/main/java/com/thealgorithms/physics/SnellLaw.java
new file mode 100644
index 000000000000..2736984814fd
--- /dev/null
+++ b/src/main/java/com/thealgorithms/physics/SnellLaw.java
@@ -0,0 +1,33 @@
+package com.thealgorithms.physics;
+
+/**
+ * Calculates refraction angle using Snell's Law:
+ * n1 * sin(theta1) = n2 * sin(theta2)
+ * @see Snell's Law
+ */
+public final class SnellLaw {
+
+ private SnellLaw() {
+ throw new AssertionError("No instances.");
+ }
+
+ /**
+ * Computes the refracted angle (theta2) in radians.
+ *
+ * @param n1 index of refraction of medium 1
+ * @param n2 index of refraction of medium 2
+ * @param theta1 incident angle in radians
+ * @return refracted angle (theta2) in radians
+ * @throws IllegalArgumentException if total internal reflection occurs
+ */
+ public static double refractedAngle(double n1, double n2, double theta1) {
+ double ratio = n1 / n2;
+ double sinTheta2 = ratio * Math.sin(theta1);
+
+ if (Math.abs(sinTheta2) > 1.0) {
+ throw new IllegalArgumentException("Total internal reflection: no refraction possible.");
+ }
+
+ return Math.asin(sinTheta2);
+ }
+}
diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java b/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java
deleted file mode 100644
index fce665c4de00..000000000000
--- a/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package com.thealgorithms.puzzlesandgames;
-
-/**
- * A class that provides methods to solve Sudoku puzzles of any n x n size
- * using a backtracking approach, where n must be a perfect square.
- * The algorithm checks for safe number placements in rows, columns,
- * and subgrids (which are sqrt(n) x sqrt(n) in size) and recursively solves the puzzle.
- * Though commonly used for 9x9 grids, it is adaptable to other valid Sudoku dimensions.
- */
-final class Sudoku {
-
- private Sudoku() {
- }
-
- /**
- * Checks if placing a number in a specific position on the Sudoku board is safe.
- * The number is considered safe if it does not violate any of the Sudoku rules:
- * - It should not be present in the same row.
- * - It should not be present in the same column.
- * - It should not be present in the corresponding 3x3 subgrid.
- * - It should not be present in the corresponding subgrid, which is sqrt(n) x sqrt(n) in size (e.g., for a 9x9 grid, the subgrid will be 3x3).
- *
- * @param board The current state of the Sudoku board.
- * @param row The row index where the number is to be placed.
- * @param col The column index where the number is to be placed.
- * @param num The number to be placed on the board.
- * @return True if the placement is safe, otherwise false.
- */
- public static boolean isSafe(int[][] board, int row, int col, int num) {
- // Check the row for duplicates
- for (int d = 0; d < board.length; d++) {
- if (board[row][d] == num) {
- return false;
- }
- }
-
- // Check the column for duplicates
- for (int r = 0; r < board.length; r++) {
- if (board[r][col] == num) {
- return false;
- }
- }
-
- // Check the corresponding 3x3 subgrid for duplicates
- int sqrt = (int) Math.sqrt(board.length);
- int boxRowStart = row - row % sqrt;
- int boxColStart = col - col % sqrt;
-
- for (int r = boxRowStart; r < boxRowStart + sqrt; r++) {
- for (int d = boxColStart; d < boxColStart + sqrt; d++) {
- if (board[r][d] == num) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Solves the Sudoku puzzle using backtracking.
- * The algorithm finds an empty cell and tries placing numbers
- * from 1 to n, where n is the size of the board
- * (for example, from 1 to 9 in a standard 9x9 Sudoku).
- * The algorithm finds an empty cell and tries placing numbers from 1 to 9.
- * The standard version of Sudoku uses numbers from 1 to 9, so the algorithm can be
- * easily modified for other variations of the game.
- * If a number placement is valid (checked via `isSafe`), the number is
- * placed and the function recursively attempts to solve the rest of the puzzle.
- * If no solution is possible, the number is removed (backtracked),
- * and the process is repeated.
- *
- * @param board The current state of the Sudoku board.
- * @param n The size of the Sudoku board (typically 9 for a standard puzzle).
- * @return True if the Sudoku puzzle is solvable, false otherwise.
- */
- public static boolean solveSudoku(int[][] board, int n) {
- int row = -1;
- int col = -1;
- boolean isEmpty = true;
-
- // Find the next empty cell
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < n; j++) {
- if (board[i][j] == 0) {
- row = i;
- col = j;
- isEmpty = false;
- break;
- }
- }
- if (!isEmpty) {
- break;
- }
- }
-
- // No empty space left
- if (isEmpty) {
- return true;
- }
-
- // Try placing numbers 1 to n in the empty cell (n should be a perfect square)
- // Eg: n=9 for a standard 9x9 Sudoku puzzle, n=16 for a 16x16 puzzle, etc.
- for (int num = 1; num <= n; num++) {
- if (isSafe(board, row, col, num)) {
- board[row][col] = num;
- if (solveSudoku(board, n)) {
- return true;
- } else {
- // replace it
- board[row][col] = 0;
- }
- }
- }
- return false;
- }
-
- /**
- * Prints the current state of the Sudoku board in a readable format.
- * Each row is printed on a new line, with numbers separated by spaces.
- *
- * @param board The current state of the Sudoku board.
- * @param n The size of the Sudoku board (typically 9 for a standard puzzle).
- */
- public static void print(int[][] board, int n) {
- // Print the board in a nxn grid format
- // if n=9, print the board in a 9x9 grid format
- // if n=16, print the board in a 16x16 grid format
- for (int r = 0; r < n; r++) {
- for (int d = 0; d < n; d++) {
- System.out.print(board[r][d]);
- System.out.print(" ");
- }
- System.out.print("\n");
-
- if ((r + 1) % (int) Math.sqrt(n) == 0) {
- System.out.print("");
- }
- }
- }
-
- /**
- * The driver method to demonstrate solving a Sudoku puzzle.
- * A sample 9x9 Sudoku puzzle is provided, and the program attempts to solve it
- * using the `solveSudoku` method. If a solution is found, it is printed to the console.
- *
- * @param args Command-line arguments (not used in this program).
- */
- public static void main(String[] args) {
- int[][] board = new int[][] {
- {3, 0, 6, 5, 0, 8, 4, 0, 0},
- {5, 2, 0, 0, 0, 0, 0, 0, 0},
- {0, 8, 7, 0, 0, 0, 0, 3, 1},
- {0, 0, 3, 0, 1, 0, 0, 8, 0},
- {9, 0, 0, 8, 6, 3, 0, 0, 5},
- {0, 5, 0, 0, 9, 0, 6, 0, 0},
- {1, 3, 0, 0, 0, 0, 2, 5, 0},
- {0, 0, 0, 0, 0, 0, 0, 7, 4},
- {0, 0, 5, 2, 0, 6, 3, 0, 0},
- };
- int n = board.length;
-
- if (solveSudoku(board, n)) {
- print(board, n);
- } else {
- System.out.println("No solution");
- }
- }
-}
diff --git a/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java b/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java
index 05d7abbbcd6c..06101295e880 100644
--- a/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java
+++ b/src/main/java/com/thealgorithms/randomized/MonteCarloIntegration.java
@@ -64,13 +64,21 @@ private static double doApproximate(Function fx, double a, doubl
if (!validate(fx, a, b, n)) {
throw new IllegalArgumentException("Invalid input parameters");
}
- double totalArea = 0.0;
+ double total = 0.0;
double interval = b - a;
- for (int i = 0; i < n; i++) {
+ int pairs = n / 2;
+ for (int i = 0; i < pairs; i++) {
+ double u = generator.nextDouble();
+ double x1 = a + u * interval;
+ double x2 = a + (1.0 - u) * interval;
+ total += fx.apply(x1);
+ total += fx.apply(x2);
+ }
+ if ((n & 1) == 1) {
double x = a + generator.nextDouble() * interval;
- totalArea += fx.apply(x);
+ total += fx.apply(x);
}
- return interval * totalArea / n;
+ return interval * total / n;
}
private static boolean validate(Function fx, double a, double b, int n) {
diff --git a/src/main/java/com/thealgorithms/searches/ExponentalSearch.java b/src/main/java/com/thealgorithms/searches/ExponentialSearch.java
similarity index 100%
rename from src/main/java/com/thealgorithms/searches/ExponentalSearch.java
rename to src/main/java/com/thealgorithms/searches/ExponentialSearch.java
diff --git a/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java b/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java
index b40c7554d84b..ecdcd36adf21 100644
--- a/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java
+++ b/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java
@@ -13,7 +13,7 @@
* In this two pointers are taken, the first points to the 0th row and the second one points to end
* column, and then the element corresponding to the pointers placed in the array is compared with
* the target that either its equal, greater or smaller than the target. If the element is equal to
- * the target, the co-ordinates of that element is returned i.e. an array of the two pointers will
+ * the target, the coordinates of that element is returned i.e. an array of the two pointers will
* be returned, else if the target is greater than corresponding element then the pointer pointing
* to the 0th row will be incremented by 1, else if the target is lesser than the corresponding
* element then the pointer pointing to the end column will be decremented by 1. And if the element
diff --git a/src/main/java/com/thealgorithms/sorts/BubbleSort.java b/src/main/java/com/thealgorithms/sorts/BubbleSort.java
index 6823c68d0a74..d2eca3506c2d 100644
--- a/src/main/java/com/thealgorithms/sorts/BubbleSort.java
+++ b/src/main/java/com/thealgorithms/sorts/BubbleSort.java
@@ -10,6 +10,13 @@ class BubbleSort implements SortAlgorithm {
/**
* Implements generic bubble sort algorithm.
*
+ * Time Complexity:
+ * - Best case: O(n) β array is already sorted.
+ * - Average case: O(n^2)
+ * - Worst case: O(n^2)
+ *
+ * Space Complexity: O(1) β in-place sorting.
+ *
* @param array the array to be sorted.
* @param the type of elements in the array.
* @return the sorted array.
diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java
index e798fb91b925..5e3b20f43e10 100644
--- a/src/main/java/com/thealgorithms/sorts/HeapSort.java
+++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java
@@ -1,9 +1,20 @@
package com.thealgorithms.sorts;
/**
- * Heap Sort Algorithm Implementation
+ * Heap Sort algorithm implementation.
+ *
+ * Heap sort converts the array into a max-heap and repeatedly extracts the maximum
+ * element to sort the array in increasing order.
+ *
+ * Time Complexity:
+ * - Best case: O(n log n)
+ * - Average case: O(n log n)
+ * - Worst case: O(n log n)
+ *
+ * Space Complexity: O(1) β in-place sorting
*
* @see Heap Sort Algorithm
+ * @see SortAlgorithm
*/
public class HeapSort implements SortAlgorithm {
diff --git a/src/main/java/com/thealgorithms/sorts/InsertionSort.java b/src/main/java/com/thealgorithms/sorts/InsertionSort.java
index 21ebf3827b5f..fdbfd9cd1cfa 100644
--- a/src/main/java/com/thealgorithms/sorts/InsertionSort.java
+++ b/src/main/java/com/thealgorithms/sorts/InsertionSort.java
@@ -1,5 +1,23 @@
package com.thealgorithms.sorts;
+/**
+ * Generic Insertion Sort algorithm.
+ *
+ * Standard insertion sort iterates through the array and inserts each element into its
+ * correct position in the sorted portion of the array.
+ *
+ * Sentinel sort is a variation that first places the minimum element at index 0 to
+ * avoid redundant comparisons in subsequent passes.
+ *
+ * Time Complexity:
+ * - Best case: O(n) β array is already sorted (sentinel sort can improve slightly)
+ * - Average case: O(n^2)
+ * - Worst case: O(n^2) β array is reverse sorted
+ *
+ * Space Complexity: O(1) β in-place sorting
+ *
+ * @see SortAlgorithm
+ */
class InsertionSort implements SortAlgorithm {
/**
diff --git a/src/main/java/com/thealgorithms/sorts/LinkListSort.java b/src/main/java/com/thealgorithms/sorts/LinkListSort.java
index 800d78f36549..d8fd76a86236 100644
--- a/src/main/java/com/thealgorithms/sorts/LinkListSort.java
+++ b/src/main/java/com/thealgorithms/sorts/LinkListSort.java
@@ -106,7 +106,7 @@ public static boolean isSorted(int[] p, int option) {
// The given array and the expected array is checked if both are same then true
// is displayed else false is displayed
default:
- // default is used incase user puts a unauthorized value
+ // default is used in case user puts a unauthorized value
System.out.println("Wrong choice");
}
// Switch case is used to call the classes as per the user requirement
diff --git a/src/main/java/com/thealgorithms/sorts/MergeSort.java b/src/main/java/com/thealgorithms/sorts/MergeSort.java
index 86a184f67b26..f7a7c8da004d 100644
--- a/src/main/java/com/thealgorithms/sorts/MergeSort.java
+++ b/src/main/java/com/thealgorithms/sorts/MergeSort.java
@@ -13,11 +13,16 @@ class MergeSort implements SortAlgorithm {
private Comparable[] aux;
/**
- * Generic merge sort algorithm implements.
+ * Generic merge sort algorithm.
*
- * @param unsorted the array which should be sorted.
- * @param Comparable class.
- * @return sorted array.
+ * Time Complexity:
+ * - Best case: O(n log n)
+ * - Average case: O(n log n)
+ * - Worst case: O(n log n)
+ *
+ * Space Complexity: O(n) β requires auxiliary array for merging.
+ *
+ * @see SortAlgorithm
*/
@Override
public > T[] sort(T[] unsorted) {
diff --git a/src/main/java/com/thealgorithms/sorts/QuickSort.java b/src/main/java/com/thealgorithms/sorts/QuickSort.java
index 3abb1aae2306..a025e6909259 100644
--- a/src/main/java/com/thealgorithms/sorts/QuickSort.java
+++ b/src/main/java/com/thealgorithms/sorts/QuickSort.java
@@ -8,9 +8,16 @@
class QuickSort implements SortAlgorithm {
/**
- * This method implements the Generic Quick Sort
+ * Generic Quick Sort algorithm.
*
- * @param array The array to be sorted Sorts the array in increasing order
+ * Time Complexity:
+ * - Best case: O(n log n) β pivot splits array roughly in half each time.
+ * - Average case: O(n log n)
+ * - Worst case: O(n^2) β occurs when pivot consistently produces unbalanced splits.
+ *
+ * Space Complexity: O(log n) β recursion stack, in-place sorting.
+ *
+ * @see SortAlgorithm
*/
@Override
public > T[] sort(T[] array) {
diff --git a/src/main/java/com/thealgorithms/sorts/SelectionSort.java b/src/main/java/com/thealgorithms/sorts/SelectionSort.java
index db7732d7e218..2d1814441701 100644
--- a/src/main/java/com/thealgorithms/sorts/SelectionSort.java
+++ b/src/main/java/com/thealgorithms/sorts/SelectionSort.java
@@ -2,11 +2,16 @@
public class SelectionSort implements SortAlgorithm {
/**
- * Sorts an array of comparable elements in increasing order using the selection sort algorithm.
+ * Generic Selection Sort algorithm.
*
- * @param array the array to be sorted
- * @param the class of array elements
- * @return the sorted array
+ * Time Complexity:
+ * - Best case: O(n^2)
+ * - Average case: O(n^2)
+ * - Worst case: O(n^2)
+ *
+ * Space Complexity: O(1) β in-place sorting.
+ *
+ * @see SortAlgorithm
*/
@Override
public > T[] sort(T[] array) {
diff --git a/src/main/java/com/thealgorithms/sorts/TopologicalSort.java b/src/main/java/com/thealgorithms/sorts/TopologicalSort.java
index e4ed240a9947..382ddde9a6f2 100644
--- a/src/main/java/com/thealgorithms/sorts/TopologicalSort.java
+++ b/src/main/java/com/thealgorithms/sorts/TopologicalSort.java
@@ -11,9 +11,16 @@
* a linked list. A Directed Graph is proven to be acyclic when a DFS or Depth First Search is
* performed, yielding no back-edges.
*
- * https://en.wikipedia.org/wiki/Topological_sorting
+ * Time Complexity: O(V + E)
+ * - V: number of vertices
+ * - E: number of edges
*
- * @author Jonathan Taylor (https://github.com/Jtmonument)
+ * Space Complexity: O(V + E)
+ * - adjacency list and recursion stack in DFS
+ *
+ * Reference: https://en.wikipedia.org/wiki/Topological_sorting
+ *
+ * Author: Jonathan Taylor (https://github.com/Jtmonument)
* Based on Introduction to Algorithms 3rd Edition
*/
public final class TopologicalSort {
diff --git a/src/main/java/com/thealgorithms/stacks/TrappingRainwater.java b/src/main/java/com/thealgorithms/stacks/TrappingRainwater.java
new file mode 100644
index 000000000000..072665061b8e
--- /dev/null
+++ b/src/main/java/com/thealgorithms/stacks/TrappingRainwater.java
@@ -0,0 +1,48 @@
+package com.thealgorithms.stacks;
+/**
+ * Trapping Rainwater Problem
+ * Given an array of non-negative integers representing the height of bars,
+ * compute how much water it can trap after raining.
+ *
+ * Example:
+ * Input: [4,2,0,3,2,5]
+ * Output: 9
+ *
+ * Time Complexity: O(n)
+ * Space Complexity: O(1)
+ *
+ * Reference: https://en.wikipedia.org/wiki/Trapping_rain_water
+ */
+public final class TrappingRainwater {
+
+ private TrappingRainwater() {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ public static int trap(int[] height) {
+ int left = 0;
+ int right = height.length - 1;
+ int leftMax = 0;
+ int rightMax = 0;
+ int result = 0;
+
+ while (left < right) {
+ if (height[left] < height[right]) {
+ if (height[left] >= leftMax) {
+ leftMax = height[left];
+ } else {
+ result += leftMax - height[left];
+ }
+ left++;
+ } else {
+ if (height[right] >= rightMax) {
+ rightMax = height[right];
+ } else {
+ result += rightMax - height[right];
+ }
+ right--;
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/stacks/ValidParentheses.java b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java
new file mode 100644
index 000000000000..2cc616a38826
--- /dev/null
+++ b/src/main/java/com/thealgorithms/stacks/ValidParentheses.java
@@ -0,0 +1,74 @@
+package com.thealgorithms.stacks;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * Valid Parentheses Problem
+ *
+ * Given a string containing just the characters '(', ')', '{', '}', '[' and ']',
+ * determine if the input string is valid.
+ *
+ * An input string is valid if:
+ * 1. Open brackets must be closed by the same type of brackets.
+ * 2. Open brackets must be closed in the correct order.
+ * 3. Every close bracket has a corresponding open bracket of the same type.
+ *
+ * Examples:
+ * Input: "()"
+ * Output: true
+ *
+ * Input: "()[]{}"
+ * Output: true
+ *
+ * Input: "(]"
+ * Output: false
+ *
+ * Input: "([)]"
+ * Output: false
+ *
+ * @author Gokul45-45
+ */
+public final class ValidParentheses {
+ private ValidParentheses() {
+ }
+
+ /**
+ * Checks if the given string has valid parentheses
+ *
+ * @param s the input string containing parentheses
+ * @return true if valid, false otherwise
+ */
+ public static boolean isValid(String s) {
+ if (s == null || s.length() % 2 != 0) {
+ return false;
+ }
+
+ Map parenthesesMap = new HashMap<>();
+ parenthesesMap.put('(', ')');
+ parenthesesMap.put('{', '}');
+ parenthesesMap.put('[', ']');
+
+ Stack stack = new Stack<>();
+
+ for (char c : s.toCharArray()) {
+ if (parenthesesMap.containsKey(c)) {
+ // Opening bracket - push to stack
+ stack.push(c);
+ } else {
+ // Closing bracket - check if it matches
+ if (stack.isEmpty()) {
+ return false;
+ }
+ char openBracket = stack.pop();
+ if (parenthesesMap.get(openBracket) != c) {
+ return false;
+ }
+ }
+ }
+
+ // Stack should be empty if all brackets are matched
+ return stack.isEmpty();
+ }
+}
diff --git a/src/main/java/com/thealgorithms/strings/Pangram.java b/src/main/java/com/thealgorithms/strings/Pangram.java
index 01307b28f6c6..a92c282d7e52 100644
--- a/src/main/java/com/thealgorithms/strings/Pangram.java
+++ b/src/main/java/com/thealgorithms/strings/Pangram.java
@@ -60,7 +60,7 @@ public static boolean isPangram(String s) {
}
/**
- * Checks if a String is Pangram or not by checking if each alhpabet is present or not
+ * Checks if a String is Pangram or not by checking if each alphabet is present or not
*
* @param s The String to check
* @return {@code true} if s is a Pangram, otherwise {@code false}
diff --git a/src/main/java/com/thealgorithms/strings/Upper.java b/src/main/java/com/thealgorithms/strings/Upper.java
index 5e248cb6ee39..85db7d41e1aa 100644
--- a/src/main/java/com/thealgorithms/strings/Upper.java
+++ b/src/main/java/com/thealgorithms/strings/Upper.java
@@ -15,23 +15,27 @@ public static void main(String[] args) {
}
/**
- * Converts all the characters in this {@code String} to upper case
+ * Converts all the characters in this {@code String} to upper case.
*
* @param s the string to convert
* @return the {@code String}, converted to uppercase.
*/
public static String toUpperCase(String s) {
if (s == null) {
- throw new IllegalArgumentException("Input string connot be null");
+ throw new IllegalArgumentException("Input string cannot be null");
}
if (s.isEmpty()) {
return s;
}
- StringBuilder result = new StringBuilder(s);
- for (int i = 0; i < result.length(); ++i) {
- char currentChar = result.charAt(i);
- if (Character.isLetter(currentChar) && Character.isLowerCase(currentChar)) {
- result.setCharAt(i, Character.toUpperCase(currentChar));
+
+ StringBuilder result = new StringBuilder(s.length());
+
+ for (int i = 0; i < s.length(); ++i) {
+ char currentChar = s.charAt(i);
+ if (Character.isLowerCase(currentChar)) {
+ result.append(Character.toUpperCase(currentChar));
+ } else {
+ result.append(currentChar);
}
}
return result.toString();
diff --git a/src/main/java/com/thealgorithms/strings/ZAlgorithm.java b/src/main/java/com/thealgorithms/strings/ZAlgorithm.java
new file mode 100644
index 000000000000..dc029b751f45
--- /dev/null
+++ b/src/main/java/com/thealgorithms/strings/ZAlgorithm.java
@@ -0,0 +1,48 @@
+/*
+ * https://en.wikipedia.org/wiki/Z-algorithm
+ */
+package com.thealgorithms.strings;
+
+public final class ZAlgorithm {
+
+ private ZAlgorithm() {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ public static int[] zFunction(String s) {
+ int n = s.length();
+ int[] z = new int[n];
+ int l = 0;
+ int r = 0;
+
+ for (int i = 1; i < n; i++) {
+ if (i <= r) {
+ z[i] = Math.min(r - i + 1, z[i - l]);
+ }
+
+ while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) {
+ z[i]++;
+ }
+
+ if (i + z[i] - 1 > r) {
+ l = i;
+ r = i + z[i] - 1;
+ }
+ }
+
+ return z;
+ }
+
+ public static int search(String text, String pattern) {
+ String s = pattern + "$" + text;
+ int[] z = zFunction(s);
+ int p = pattern.length();
+
+ for (int i = 0; i < z.length; i++) {
+ if (z[i] == p) {
+ return i - p - 1;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/src/test/java/com/thealgorithms/backtracking/CombinationSumTest.java b/src/test/java/com/thealgorithms/backtracking/CombinationSumTest.java
new file mode 100644
index 000000000000..986c71acebe8
--- /dev/null
+++ b/src/test/java/com/thealgorithms/backtracking/CombinationSumTest.java
@@ -0,0 +1,29 @@
+package com.thealgorithms.backtracking;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class CombinationSumTest {
+ private static List> norm(Iterable> x) {
+ List> y = new ArrayList<>();
+ for (var p : x) {
+ var q = new ArrayList<>(p);
+ q.sort(Integer::compare);
+ y.add(q);
+ }
+ y.sort(Comparator.>comparingInt(List::size).thenComparing(Object::toString));
+ return y;
+ }
+
+ @Test
+ void sample() {
+ int[] candidates = {2, 3, 6, 7};
+ int target = 7;
+ var expected = List.of(List.of(2, 2, 3), List.of(7));
+ assertEquals(norm(expected), norm(CombinationSum.combinationSum(candidates, target)));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java b/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java
new file mode 100644
index 000000000000..75d3eae08629
--- /dev/null
+++ b/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java
@@ -0,0 +1,53 @@
+package com.thealgorithms.backtracking;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class SudokuSolverTest {
+
+ @Test
+ void testSolveSudokuEasyPuzzle() {
+ int[][] board = {{5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9, 5, 0, 0, 0}, {0, 9, 8, 0, 0, 0, 0, 6, 0}, {8, 0, 0, 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0, 3, 0, 0, 1}, {7, 0, 0, 0, 2, 0, 0, 0, 6}, {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1, 9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9}};
+
+ assertTrue(SudokuSolver.solveSudoku(board));
+
+ int[][] expected = {{5, 3, 4, 6, 7, 8, 9, 1, 2}, {6, 7, 2, 1, 9, 5, 3, 4, 8}, {1, 9, 8, 3, 4, 2, 5, 6, 7}, {8, 5, 9, 7, 6, 1, 4, 2, 3}, {4, 2, 6, 8, 5, 3, 7, 9, 1}, {7, 1, 3, 9, 2, 4, 8, 5, 6}, {9, 6, 1, 5, 3, 7, 2, 8, 4}, {2, 8, 7, 4, 1, 9, 6, 3, 5}, {3, 4, 5, 2, 8, 6, 1, 7, 9}};
+
+ assertArrayEquals(expected, board);
+ }
+
+ @Test
+ void testSolveSudokuHardPuzzle() {
+ int[][] board = {{0, 0, 0, 0, 0, 0, 6, 8, 0}, {0, 0, 0, 0, 7, 3, 0, 0, 9}, {3, 0, 9, 0, 0, 0, 0, 4, 5}, {4, 9, 0, 0, 0, 0, 0, 0, 0}, {8, 0, 3, 0, 5, 0, 9, 0, 2}, {0, 0, 0, 0, 0, 0, 0, 3, 6}, {9, 6, 0, 0, 0, 0, 3, 0, 8}, {7, 0, 0, 6, 8, 0, 0, 0, 0}, {0, 2, 8, 0, 0, 0, 0, 0, 0}};
+
+ assertTrue(SudokuSolver.solveSudoku(board));
+ }
+
+ @Test
+ void testSolveSudokuAlreadySolved() {
+ int[][] board = {{5, 3, 4, 6, 7, 8, 9, 1, 2}, {6, 7, 2, 1, 9, 5, 3, 4, 8}, {1, 9, 8, 3, 4, 2, 5, 6, 7}, {8, 5, 9, 7, 6, 1, 4, 2, 3}, {4, 2, 6, 8, 5, 3, 7, 9, 1}, {7, 1, 3, 9, 2, 4, 8, 5, 6}, {9, 6, 1, 5, 3, 7, 2, 8, 4}, {2, 8, 7, 4, 1, 9, 6, 3, 5}, {3, 4, 5, 2, 8, 6, 1, 7, 9}};
+
+ assertTrue(SudokuSolver.solveSudoku(board));
+ }
+
+ @Test
+ void testSolveSudokuInvalidSize() {
+ int[][] board = {{1, 2, 3}, {4, 5, 6}};
+ assertFalse(SudokuSolver.solveSudoku(board));
+ }
+
+ @Test
+ void testSolveSudokuNullBoard() {
+ assertFalse(SudokuSolver.solveSudoku(null));
+ }
+
+ @Test
+ void testSolveSudokuEmptyBoard() {
+ int[][] board = {{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}};
+
+ assertTrue(SudokuSolver.solveSudoku(board));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java b/src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java
new file mode 100644
index 000000000000..c8e7cd0af0dd
--- /dev/null
+++ b/src/test/java/com/thealgorithms/backtracking/UniquePermutationTest.java
@@ -0,0 +1,31 @@
+package com.thealgorithms.backtracking;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+public class UniquePermutationTest {
+
+ @Test
+ void testUniquePermutationsAab() {
+ List expected = Arrays.asList("AAB", "ABA", "BAA");
+ List result = UniquePermutation.generateUniquePermutations("AAB");
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testUniquePermutationsAbc() {
+ List expected = Arrays.asList("ABC", "ACB", "BAC", "BCA", "CAB", "CBA");
+ List result = UniquePermutation.generateUniquePermutations("ABC");
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testEmptyString() {
+ List expected = Arrays.asList("");
+ List result = UniquePermutation.generateUniquePermutations("");
+ assertEquals(expected, result);
+ }
+}
diff --git a/src/test/java/com/thealgorithms/bitmanipulation/BitRotateTest.java b/src/test/java/com/thealgorithms/bitmanipulation/BitRotateTest.java
new file mode 100644
index 000000000000..0595ae5a73e1
--- /dev/null
+++ b/src/test/java/com/thealgorithms/bitmanipulation/BitRotateTest.java
@@ -0,0 +1,205 @@
+package com.thealgorithms.bitmanipulation;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+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 org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for BitRotate class covering typical, boundary, and edge cases.
+ * Tests verify correct behavior for 32-bit circular bit rotations.
+ *
+ * @author Yajunesh
+ */
+public class BitRotateTest {
+
+ // ===== rotateLeft Tests =====
+
+ @Test
+ public void testRotateLeftBasic() {
+ // Basic left rotation
+ assertEquals(0b00000000_00000000_00000000_00000010, BitRotate.rotateLeft(1, 1));
+ assertEquals(0b00000000_00000000_00000000_00000100, BitRotate.rotateLeft(1, 2));
+ assertEquals(0b00000000_00000000_00000000_00001000, BitRotate.rotateLeft(1, 3));
+ }
+
+ @Test
+ public void testRotateLeftWithCarry() {
+ // Test bits carrying from left to right
+ // Binary: 10000000_00000000_00000000_00000001
+ int value = 0x80000001;
+ // After left rotate by 1: 00000000_00000000_00000000_00000011
+ assertEquals(3, BitRotate.rotateLeft(value, 1));
+
+ // Binary: 11000000_00000000_00000000_00000000
+ value = 0xC0000000;
+ // After left rotate by 1: 10000000_00000000_00000000_00000001
+ assertEquals(0x80000001, BitRotate.rotateLeft(value, 1));
+ }
+
+ @Test
+ public void testRotateLeftShift32() {
+ // Shift of 32 should be same as shift of 0 (modulo behavior)
+ int value = 0x12345678;
+ assertEquals(value, BitRotate.rotateLeft(value, 32));
+ assertEquals(value, BitRotate.rotateLeft(value, 64));
+ assertEquals(value, BitRotate.rotateLeft(value, 96));
+ }
+
+ @Test
+ public void testRotateLeftShiftNormalization() {
+ // Test that shifts > 32 are properly normalized
+ int value = 1;
+ assertEquals(BitRotate.rotateLeft(value, 1), BitRotate.rotateLeft(value, 33));
+ assertEquals(BitRotate.rotateLeft(value, 5), BitRotate.rotateLeft(value, 37));
+ }
+
+ @Test
+ public void testRotateLeftZeroShift() {
+ // Zero shift should return original value
+ int value = 0xABCD1234;
+ assertEquals(value, BitRotate.rotateLeft(value, 0));
+ }
+
+ // ===== rotateRight Tests =====
+
+ @Test
+ public void testRotateRightBasic() {
+ // Basic right rotation
+ assertEquals(0b10000000_00000000_00000000_00000000, BitRotate.rotateRight(1, 1));
+ assertEquals(0b01000000_00000000_00000000_00000000, BitRotate.rotateRight(1, 2));
+ assertEquals(0b00100000_00000000_00000000_00000000, BitRotate.rotateRight(1, 3));
+ }
+
+ @Test
+ public void testRotateRightWithCarry() {
+ // Test bits carrying from right to left
+ // Binary: 00000000_00000000_00000000_00000011
+ int value = 3;
+ // After right rotate by 1: 10000000_00000000_00000000_00000001
+ assertEquals(0x80000001, BitRotate.rotateRight(value, 1));
+
+ // Binary: 00000000_00000000_00000000_00000001
+ value = 1;
+ // After right rotate by 1: 10000000_00000000_00000000_00000000
+ assertEquals(0x80000000, BitRotate.rotateRight(value, 1));
+ }
+
+ @Test
+ public void testRotateRightShift32() {
+ // Shift of 32 should be same as shift of 0 (modulo behavior)
+ int value = 0x9ABCDEF0;
+ assertEquals(value, BitRotate.rotateRight(value, 32));
+ assertEquals(value, BitRotate.rotateRight(value, 64));
+ assertEquals(value, BitRotate.rotateRight(value, 96));
+ }
+
+ @Test
+ public void testRotateRightShiftNormalization() {
+ // Test that shifts > 32 are properly normalized
+ int value = 1;
+ assertEquals(BitRotate.rotateRight(value, 1), BitRotate.rotateRight(value, 33));
+ assertEquals(BitRotate.rotateRight(value, 7), BitRotate.rotateRight(value, 39));
+ }
+
+ @Test
+ public void testRotateRightZeroShift() {
+ // Zero shift should return original value
+ int value = 0xDEADBEEF;
+ assertEquals(value, BitRotate.rotateRight(value, 0));
+ }
+
+ // ===== Edge Case Tests =====
+
+ @Test
+ public void testRotateLeftMaxValue() {
+ // Test with maximum integer value
+ int value = Integer.MAX_VALUE; // 0x7FFFFFFF
+ int rotated = BitRotate.rotateLeft(value, 1);
+ // MAX_VALUE << 1 should become 0xFFFFFFFE, but with rotation it becomes different
+ assertEquals(0xFFFFFFFE, rotated);
+ }
+
+ @Test
+ public void testRotateRightMinValue() {
+ // Test with minimum integer value (treated as unsigned)
+ int value = Integer.MIN_VALUE; // 0x80000000
+ int rotated = BitRotate.rotateRight(value, 1);
+ // MIN_VALUE >>> 1 should become 0x40000000, but with rotation from left
+ assertEquals(0x40000000, rotated);
+ }
+
+ @Test
+ public void testRotateAllOnes() {
+ // Test with all bits set
+ int value = 0xFFFFFFFF; // All ones
+ assertEquals(value, BitRotate.rotateLeft(value, 13));
+ assertEquals(value, BitRotate.rotateRight(value, 27));
+ }
+
+ @Test
+ public void testRotateAllZeros() {
+ // Test with all bits zero
+ int value = 0x00000000;
+ assertEquals(value, BitRotate.rotateLeft(value, 15));
+ assertEquals(value, BitRotate.rotateRight(value, 19));
+ }
+
+ // ===== Exception Tests =====
+
+ @Test
+ public void testRotateLeftNegativeShift() {
+ // Negative shifts should throw IllegalArgumentException
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> BitRotate.rotateLeft(42, -1));
+ assertTrue(exception.getMessage().contains("negative"));
+ }
+
+ @Test
+ public void testRotateRightNegativeShift() {
+ // Negative shifts should throw IllegalArgumentException
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> BitRotate.rotateRight(42, -5));
+ assertTrue(exception.getMessage().contains("negative"));
+ }
+
+ // ===== Complementary Operations Test =====
+
+ @Test
+ public void testRotateLeftRightComposition() {
+ // Rotating left then right by same amount should return original value
+ int original = 0x12345678;
+ int shift = 7;
+
+ int leftRotated = BitRotate.rotateLeft(original, shift);
+ int restored = BitRotate.rotateRight(leftRotated, shift);
+
+ assertEquals(original, restored);
+ }
+
+ @Test
+ public void testRotateRightLeftComposition() {
+ // Rotating right then left by same amount should return original value
+ int original = 0x9ABCDEF0;
+ int shift = 13;
+
+ int rightRotated = BitRotate.rotateRight(original, shift);
+ int restored = BitRotate.rotateLeft(rightRotated, shift);
+
+ assertEquals(original, restored);
+ }
+
+ @Test
+ public void testRotateLeft31IsSameAsRotateRight1() {
+ // Rotating left by 31 should be same as rotating right by 1
+ int value = 0x55555555;
+ assertEquals(BitRotate.rotateLeft(value, 31), BitRotate.rotateRight(value, 1));
+ }
+
+ @Test
+ public void testTraversals() {
+ // Test that methods don't throw exceptions
+ assertDoesNotThrow(() -> BitRotate.rotateLeft(1, 1));
+ assertDoesNotThrow(() -> BitRotate.rotateRight(1, 1));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java
index 61e0757f9c12..757c6edc0151 100644
--- a/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java
+++ b/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java
@@ -1,26 +1,56 @@
package com.thealgorithms.bitmanipulation;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
-public class CountSetBitsTest {
+class CountSetBitsTest {
@Test
- void testSetBits() {
- CountSetBits csb = new CountSetBits();
- assertEquals(1L, csb.countSetBits(16));
- assertEquals(4, csb.countSetBits(15));
- assertEquals(5, csb.countSetBits(10000));
- assertEquals(5, csb.countSetBits(31));
+ void testCountSetBitsZero() {
+ assertEquals(0, CountSetBits.countSetBits(0));
}
@Test
- void testSetBitsLookupApproach() {
- CountSetBits csb = new CountSetBits();
- assertEquals(1L, csb.lookupApproach(16));
- assertEquals(4, csb.lookupApproach(15));
- assertEquals(5, csb.lookupApproach(10000));
- assertEquals(5, csb.lookupApproach(31));
+ void testCountSetBitsOne() {
+ assertEquals(1, CountSetBits.countSetBits(1));
+ }
+
+ @Test
+ void testCountSetBitsSmallNumber() {
+ assertEquals(4, CountSetBits.countSetBits(3)); // 1(1) + 10(1) + 11(2) = 4
+ }
+
+ @Test
+ void testCountSetBitsFive() {
+ assertEquals(7, CountSetBits.countSetBits(5)); // 1 + 1 + 2 + 1 + 2 = 7
+ }
+
+ @Test
+ void testCountSetBitsTen() {
+ assertEquals(17, CountSetBits.countSetBits(10));
+ }
+
+ @Test
+ void testCountSetBitsLargeNumber() {
+ assertEquals(42, CountSetBits.countSetBits(20)); // Changed from 93 to 42
+ }
+
+ @Test
+ void testCountSetBitsPowerOfTwo() {
+ assertEquals(13, CountSetBits.countSetBits(8)); // Changed from 9 to 13
+ }
+
+ @Test
+ void testCountSetBitsNegativeInput() {
+ assertThrows(IllegalArgumentException.class, () -> CountSetBits.countSetBits(-1));
+ }
+
+ @Test
+ void testNaiveApproachMatchesOptimized() {
+ for (int i = 0; i <= 100; i++) {
+ assertEquals(CountSetBits.countSetBitsNaive(i), CountSetBits.countSetBits(i), "Mismatch at n = " + i);
+ }
}
}
diff --git a/src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java b/src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java
new file mode 100644
index 000000000000..837c56c603d4
--- /dev/null
+++ b/src/test/java/com/thealgorithms/ciphers/OneTimePadCipherTest.java
@@ -0,0 +1,49 @@
+package com.thealgorithms.ciphers;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.nio.charset.StandardCharsets;
+import org.junit.jupiter.api.Test;
+
+class OneTimePadCipherTest {
+
+ @Test
+ void encryptAndDecryptWithRandomKeyRestoresPlaintext() {
+ String plaintext = "The quick brown fox jumps over the lazy dog.";
+ byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
+
+ byte[] key = OneTimePadCipher.generateKey(plaintextBytes.length);
+
+ byte[] ciphertext = OneTimePadCipher.encrypt(plaintextBytes, key);
+ byte[] decrypted = OneTimePadCipher.decrypt(ciphertext, key);
+
+ assertArrayEquals(plaintextBytes, decrypted);
+ assertEquals(plaintext, new String(decrypted, StandardCharsets.UTF_8));
+ }
+
+ @Test
+ void generateKeyWithNegativeLengthThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> OneTimePadCipher.generateKey(-1));
+ }
+
+ @Test
+ void encryptWithMismatchedKeyLengthThrowsException() {
+ byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
+ byte[] shortKey = OneTimePadCipher.generateKey(2);
+
+ assertThrows(IllegalArgumentException.class, () -> OneTimePadCipher.encrypt(data, shortKey));
+ }
+
+ @Test
+ void decryptWithMismatchedKeyLengthThrowsException() {
+ byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
+ byte[] key = OneTimePadCipher.generateKey(data.length);
+ byte[] ciphertext = OneTimePadCipher.encrypt(data, key);
+
+ byte[] wrongSizedKey = OneTimePadCipher.generateKey(data.length + 1);
+
+ assertThrows(IllegalArgumentException.class, () -> OneTimePadCipher.decrypt(ciphertext, wrongSizedKey));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java b/src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java
new file mode 100644
index 000000000000..24d55b706f36
--- /dev/null
+++ b/src/test/java/com/thealgorithms/conversions/TemperatureConverterTest.java
@@ -0,0 +1,54 @@
+package com.thealgorithms.conversions;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class TemperatureConverterTest {
+
+ private static final double DELTA = 0.01;
+
+ @Test
+ void testCelsiusToFahrenheit() {
+ assertEquals(32.0, TemperatureConverter.celsiusToFahrenheit(0.0), DELTA);
+ assertEquals(212.0, TemperatureConverter.celsiusToFahrenheit(100.0), DELTA);
+ assertEquals(-40.0, TemperatureConverter.celsiusToFahrenheit(-40.0), DELTA);
+ assertEquals(98.6, TemperatureConverter.celsiusToFahrenheit(37.0), DELTA);
+ }
+
+ @Test
+ void testCelsiusToKelvin() {
+ assertEquals(273.15, TemperatureConverter.celsiusToKelvin(0.0), DELTA);
+ assertEquals(373.15, TemperatureConverter.celsiusToKelvin(100.0), DELTA);
+ assertEquals(233.15, TemperatureConverter.celsiusToKelvin(-40.0), DELTA);
+ }
+
+ @Test
+ void testFahrenheitToCelsius() {
+ assertEquals(0.0, TemperatureConverter.fahrenheitToCelsius(32.0), DELTA);
+ assertEquals(100.0, TemperatureConverter.fahrenheitToCelsius(212.0), DELTA);
+ assertEquals(-40.0, TemperatureConverter.fahrenheitToCelsius(-40.0), DELTA);
+ assertEquals(37.0, TemperatureConverter.fahrenheitToCelsius(98.6), DELTA);
+ }
+
+ @Test
+ void testFahrenheitToKelvin() {
+ assertEquals(273.15, TemperatureConverter.fahrenheitToKelvin(32.0), DELTA);
+ assertEquals(373.15, TemperatureConverter.fahrenheitToKelvin(212.0), DELTA);
+ assertEquals(233.15, TemperatureConverter.fahrenheitToKelvin(-40.0), DELTA);
+ }
+
+ @Test
+ void testKelvinToCelsius() {
+ assertEquals(0.0, TemperatureConverter.kelvinToCelsius(273.15), DELTA);
+ assertEquals(100.0, TemperatureConverter.kelvinToCelsius(373.15), DELTA);
+ assertEquals(-273.15, TemperatureConverter.kelvinToCelsius(0.0), DELTA);
+ }
+
+ @Test
+ void testKelvinToFahrenheit() {
+ assertEquals(32.0, TemperatureConverter.kelvinToFahrenheit(273.15), DELTA);
+ assertEquals(212.0, TemperatureConverter.kelvinToFahrenheit(373.15), DELTA);
+ assertEquals(-40.0, TemperatureConverter.kelvinToFahrenheit(233.15), DELTA);
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java
new file mode 100644
index 000000000000..c824241c680d
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/BellmanFordTest.java
@@ -0,0 +1,158 @@
+package com.thealgorithms.datastructures.graphs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for the BellmanFord algorithm implementation.
+ * Tests cover various graph scenarios including:
+ * - Simple weighted graphs
+ * - Graphs with negative weights
+ * - Single vertex graphs
+ * - Disconnected graphs
+ * - Linear path graphs
+ */
+class BellmanFordTest {
+
+ @Test
+ void testSimpleGraph() {
+ // Create a simple graph with 5 vertices and 8 edges
+ // Graph visualization:
+ // 1
+ // /|\
+ // 6 | 7
+ // / | \
+ // 0 5 2
+ // \ | /
+ // 8 | -2
+ // \|/
+ // 4---3
+ // 9
+ BellmanFord bellmanFord = new BellmanFord(5, 8);
+ bellmanFord.addEdge(0, 1, 6);
+ bellmanFord.addEdge(0, 4, 8);
+ bellmanFord.addEdge(1, 2, 7);
+ bellmanFord.addEdge(1, 4, 5);
+ bellmanFord.addEdge(2, 3, -2);
+ bellmanFord.addEdge(2, 4, -3);
+ bellmanFord.addEdge(3, 4, 9);
+ bellmanFord.addEdge(4, 3, 7);
+
+ // Verify edge array creation
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(8, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testGraphWithNegativeWeights() {
+ // Graph with negative edge weights (but no negative cycle)
+ BellmanFord bellmanFord = new BellmanFord(4, 5);
+ bellmanFord.addEdge(0, 1, 4);
+ bellmanFord.addEdge(0, 2, 5);
+ bellmanFord.addEdge(1, 2, -3);
+ bellmanFord.addEdge(2, 3, 4);
+ bellmanFord.addEdge(1, 3, 6);
+
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(5, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testSingleVertexGraph() {
+ // Graph with single vertex and no edges
+ BellmanFord bellmanFord = new BellmanFord(1, 0);
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(0, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testLinearGraph() {
+ // Linear graph: 0 -> 1 -> 2 -> 3
+ BellmanFord bellmanFord = new BellmanFord(4, 3);
+ bellmanFord.addEdge(0, 1, 2);
+ bellmanFord.addEdge(1, 2, 3);
+ bellmanFord.addEdge(2, 3, 4);
+
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(3, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testEdgeAddition() {
+ BellmanFord bellmanFord = new BellmanFord(3, 3);
+
+ bellmanFord.addEdge(0, 1, 5);
+ bellmanFord.addEdge(1, 2, 3);
+ bellmanFord.addEdge(0, 2, 10);
+
+ // Verify all edges were added
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(3, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testGraphWithZeroWeightEdges() {
+ // Graph with zero weight edges
+ BellmanFord bellmanFord = new BellmanFord(3, 3);
+ bellmanFord.addEdge(0, 1, 0);
+ bellmanFord.addEdge(1, 2, 0);
+ bellmanFord.addEdge(0, 2, 1);
+
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(3, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testLargerGraph() {
+ // Larger graph with 6 vertices
+ BellmanFord bellmanFord = new BellmanFord(6, 9);
+ bellmanFord.addEdge(0, 1, 5);
+ bellmanFord.addEdge(0, 2, 3);
+ bellmanFord.addEdge(1, 3, 6);
+ bellmanFord.addEdge(1, 2, 2);
+ bellmanFord.addEdge(2, 4, 4);
+ bellmanFord.addEdge(2, 5, 2);
+ bellmanFord.addEdge(2, 3, 7);
+ bellmanFord.addEdge(3, 4, -1);
+ bellmanFord.addEdge(4, 5, -2);
+
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(9, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testVertexAndEdgeCount() {
+ BellmanFord bellmanFord = new BellmanFord(10, 15);
+ assertEquals(10, bellmanFord.vertex);
+ assertEquals(15, bellmanFord.edge);
+ }
+
+ @Test
+ void testMultipleEdgesBetweenSameVertices() {
+ // Graph allowing multiple edges between same vertices
+ BellmanFord bellmanFord = new BellmanFord(2, 3);
+ bellmanFord.addEdge(0, 1, 5);
+ bellmanFord.addEdge(0, 1, 3);
+ bellmanFord.addEdge(1, 0, 2);
+
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(3, bellmanFord.getEdgeArray().length);
+ }
+
+ @Test
+ void testCompleteGraph() {
+ // Complete graph with 4 vertices (6 edges for undirected equivalent)
+ BellmanFord bellmanFord = new BellmanFord(4, 6);
+ bellmanFord.addEdge(0, 1, 1);
+ bellmanFord.addEdge(0, 2, 2);
+ bellmanFord.addEdge(0, 3, 3);
+ bellmanFord.addEdge(1, 2, 4);
+ bellmanFord.addEdge(1, 3, 5);
+ bellmanFord.addEdge(2, 3, 6);
+
+ assertNotNull(bellmanFord.getEdgeArray());
+ assertEquals(6, bellmanFord.getEdgeArray().length);
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java
new file mode 100644
index 000000000000..b5cfdd9de04f
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/ConnectedComponentTest.java
@@ -0,0 +1,204 @@
+package com.thealgorithms.datastructures.graphs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for the Graph class in ConnectedComponent.java.
+ * Tests the depth-first search implementation and connected component counting.
+ * Covers various graph topologies including:
+ * - Single connected components
+ * - Multiple disconnected components
+ * - Self-loops
+ * - Linear chains
+ * - Cyclic graphs
+ */
+class ConnectedComponentTest {
+
+ @Test
+ void testSingleConnectedComponent() {
+ Graph graph = new Graph<>();
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ graph.addEdge(3, 4);
+ graph.addEdge(4, 1);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testTwoDisconnectedComponents() {
+ Graph graph = new Graph<>();
+ // Component 1: 1-2-3
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ // Component 2: 4-5
+ graph.addEdge(4, 5);
+
+ assertEquals(2, graph.countGraphs());
+ }
+
+ @Test
+ void testThreeDisconnectedComponents() {
+ Graph graph = new Graph<>();
+ // Component 1: a-b-c-d-e
+ graph.addEdge('a', 'b');
+ graph.addEdge('a', 'e');
+ graph.addEdge('b', 'e');
+ graph.addEdge('b', 'c');
+ graph.addEdge('c', 'd');
+ graph.addEdge('d', 'a');
+ // Component 2: x-y-z
+ graph.addEdge('x', 'y');
+ graph.addEdge('x', 'z');
+ // Component 3: w (self-loop)
+ graph.addEdge('w', 'w');
+
+ assertEquals(3, graph.countGraphs());
+ }
+
+ @Test
+ void testSingleNodeSelfLoop() {
+ Graph graph = new Graph<>();
+ graph.addEdge(1, 1);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testLinearChain() {
+ Graph graph = new Graph<>();
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ graph.addEdge(3, 4);
+ graph.addEdge(4, 5);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testStarTopology() {
+ // Star graph with center node 0 connected to nodes 1, 2, 3, 4
+ Graph graph = new Graph<>();
+ graph.addEdge(0, 1);
+ graph.addEdge(0, 2);
+ graph.addEdge(0, 3);
+ graph.addEdge(0, 4);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testCompleteGraph() {
+ // Complete graph K4: every node connected to every other node
+ Graph graph = new Graph<>();
+ graph.addEdge(1, 2);
+ graph.addEdge(1, 3);
+ graph.addEdge(1, 4);
+ graph.addEdge(2, 3);
+ graph.addEdge(2, 4);
+ graph.addEdge(3, 4);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testStringVertices() {
+ Graph graph = new Graph<>();
+ // Component 1
+ graph.addEdge("New York", "Los Angeles");
+ graph.addEdge("Los Angeles", "Chicago");
+ // Component 2
+ graph.addEdge("London", "Paris");
+ // Component 3
+ graph.addEdge("Tokyo", "Tokyo");
+
+ assertEquals(3, graph.countGraphs());
+ }
+
+ @Test
+ void testEmptyGraph() {
+ Graph graph = new Graph<>();
+ assertEquals(0, graph.countGraphs());
+ }
+
+ @Test
+ void testDepthFirstSearchBasic() {
+ Graph graph = new Graph<>();
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+
+ // Get the first node and perform DFS
+ assertNotNull(graph.nodeList);
+ assertEquals(3, graph.nodeList.size());
+ }
+
+ @Test
+ void testManyIsolatedComponents() {
+ Graph graph = new Graph<>();
+ // Create 5 isolated components (each is a self-loop)
+ graph.addEdge(1, 1);
+ graph.addEdge(2, 2);
+ graph.addEdge(3, 3);
+ graph.addEdge(4, 4);
+ graph.addEdge(5, 5);
+
+ assertEquals(5, graph.countGraphs());
+ }
+
+ @Test
+ void testBidirectionalEdges() {
+ Graph graph = new Graph<>();
+ // Note: This is a directed graph representation
+ // Adding edge 1->2 does not automatically add 2->1
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 1);
+ graph.addEdge(2, 3);
+ graph.addEdge(3, 2);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testCyclicGraph() {
+ Graph graph = new Graph<>();
+ // Create a cycle: 1 -> 2 -> 3 -> 4 -> 1
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ graph.addEdge(3, 4);
+ graph.addEdge(4, 1);
+
+ assertEquals(1, graph.countGraphs());
+ }
+
+ @Test
+ void testMultipleCycles() {
+ Graph graph = new Graph<>();
+ // Cycle 1: 1 -> 2 -> 3 -> 1
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ graph.addEdge(3, 1);
+ // Cycle 2: 4 -> 5 -> 4
+ graph.addEdge(4, 5);
+ graph.addEdge(5, 4);
+
+ assertEquals(2, graph.countGraphs());
+ }
+
+ @Test
+ void testIntegerGraphFromMainExample() {
+ // Recreate the example from main method
+ Graph graph = new Graph<>();
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ graph.addEdge(2, 4);
+ graph.addEdge(3, 5);
+ graph.addEdge(7, 8);
+ graph.addEdge(8, 10);
+ graph.addEdge(10, 8);
+
+ assertEquals(2, graph.countGraphs());
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java
index c5df9acdf33b..a189091c17d3 100644
--- a/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java
@@ -1,6 +1,7 @@
package com.thealgorithms.datastructures.graphs;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeEach;
@@ -61,4 +62,120 @@ void testInvalidSourceVertex() {
assertThrows(IllegalArgumentException.class, () -> dijkstraAlgorithm.run(graph, -1));
assertThrows(IllegalArgumentException.class, () -> dijkstraAlgorithm.run(graph, graph.length));
}
+
+ @Test
+ void testLinearGraph() {
+ // Linear graph: 0 - 1 - 2 - 3
+ // with weights: 2 3 4
+ int[][] linearGraph = {{0, 2, 0, 0}, {2, 0, 3, 0}, {0, 3, 0, 4}, {0, 0, 4, 0}};
+
+ DijkstraAlgorithm dijkstraLinear = new DijkstraAlgorithm(4);
+ int[] distances = dijkstraLinear.run(linearGraph, 0);
+
+ assertArrayEquals(new int[] {0, 2, 5, 9}, distances);
+ }
+
+ @Test
+ void testStarTopology() {
+ // Star graph: center node 0 connected to all others
+ // 1(2)
+ // |
+ // 3(4)-0-2(3)
+ // |
+ // 4(5)
+ int[][] starGraph = {{0, 2, 3, 4, 5}, {2, 0, 0, 0, 0}, {3, 0, 0, 0, 0}, {4, 0, 0, 0, 0}, {5, 0, 0, 0, 0}};
+
+ DijkstraAlgorithm dijkstraStar = new DijkstraAlgorithm(5);
+ int[] distances = dijkstraStar.run(starGraph, 0);
+
+ assertArrayEquals(new int[] {0, 2, 3, 4, 5}, distances);
+ }
+
+ @Test
+ void testCompleteGraphK4() {
+ // Complete graph K4 with varying weights
+ int[][] completeGraph = {{0, 1, 2, 3}, {1, 0, 4, 5}, {2, 4, 0, 6}, {3, 5, 6, 0}};
+
+ DijkstraAlgorithm dijkstraComplete = new DijkstraAlgorithm(4);
+ int[] distances = dijkstraComplete.run(completeGraph, 0);
+
+ // Direct paths from 0 are shortest
+ assertArrayEquals(new int[] {0, 1, 2, 3}, distances);
+ }
+
+ @Test
+ void testDifferentSourceVertex() {
+ // Test running from different source vertices
+ int[][] simpleGraph = {{0, 5, 0, 0}, {5, 0, 3, 0}, {0, 3, 0, 2}, {0, 0, 2, 0}};
+
+ DijkstraAlgorithm dijkstra = new DijkstraAlgorithm(4);
+
+ // From vertex 0
+ int[] distFrom0 = dijkstra.run(simpleGraph, 0);
+ assertArrayEquals(new int[] {0, 5, 8, 10}, distFrom0);
+
+ // From vertex 2
+ int[] distFrom2 = dijkstra.run(simpleGraph, 2);
+ assertArrayEquals(new int[] {8, 3, 0, 2}, distFrom2);
+
+ // From vertex 3
+ int[] distFrom3 = dijkstra.run(simpleGraph, 3);
+ assertArrayEquals(new int[] {10, 5, 2, 0}, distFrom3);
+ }
+
+ @Test
+ void testUnitWeightGraph() {
+ // Graph with all unit weights (like BFS distance)
+ int[][] unitGraph = {{0, 1, 1, 0}, {1, 0, 1, 1}, {1, 1, 0, 1}, {0, 1, 1, 0}};
+
+ DijkstraAlgorithm dijkstraUnit = new DijkstraAlgorithm(4);
+ int[] distances = dijkstraUnit.run(unitGraph, 0);
+
+ assertArrayEquals(new int[] {0, 1, 1, 2}, distances);
+ }
+
+ @Test
+ void testTwoVertexGraph() {
+ int[][] twoVertexGraph = {{0, 7}, {7, 0}};
+
+ DijkstraAlgorithm dijkstraTwo = new DijkstraAlgorithm(2);
+ int[] distances = dijkstraTwo.run(twoVertexGraph, 0);
+
+ assertArrayEquals(new int[] {0, 7}, distances);
+ }
+
+ @Test
+ void testShortcutPath() {
+ // Graph where direct path is longer than indirect path
+ // 0 --(10)--> 2
+ // 0 --(1)--> 1 --(2)--> 2
+ int[][] shortcutGraph = {{0, 1, 10}, {1, 0, 2}, {10, 2, 0}};
+
+ DijkstraAlgorithm dijkstraShortcut = new DijkstraAlgorithm(3);
+ int[] distances = dijkstraShortcut.run(shortcutGraph, 0);
+
+ // The shortest path to vertex 2 should be 3 (via vertex 1), not 10 (direct)
+ assertArrayEquals(new int[] {0, 1, 3}, distances);
+ }
+
+ @Test
+ void testSourceToSourceDistanceIsZero() {
+ // Verify distance from source to itself is always 0
+ int[] distances = dijkstraAlgorithm.run(graph, 0);
+ assertEquals(0, distances[0]);
+
+ distances = dijkstraAlgorithm.run(graph, 5);
+ assertEquals(0, distances[5]);
+ }
+
+ @Test
+ void testLargeWeights() {
+ // Graph with large weights
+ int[][] largeWeightGraph = {{0, 1000, 0}, {1000, 0, 2000}, {0, 2000, 0}};
+
+ DijkstraAlgorithm dijkstraLarge = new DijkstraAlgorithm(3);
+ int[] distances = dijkstraLarge.run(largeWeightGraph, 0);
+
+ assertArrayEquals(new int[] {0, 1000, 3000}, distances);
+ }
}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java
index cc8a2df872ce..eaff0222bd36 100644
--- a/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java
@@ -137,4 +137,215 @@ void testDisconnectedGraph() {
assertTrue(dfs.containsAll(Arrays.asList(0, 1)));
assertTrue(bfs.containsAll(Arrays.asList(0, 1)));
}
+
+ @Test
+ void testSingleVertexGraphDfs() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(1);
+
+ List dfs = graph.depthFirstOrder(0);
+ assertEquals(1, dfs.size());
+ assertEquals(0, dfs.getFirst());
+ }
+
+ @Test
+ void testSingleVertexGraphBfs() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(1);
+
+ List bfs = graph.breadthFirstOrder(0);
+ assertEquals(1, bfs.size());
+ assertEquals(0, bfs.getFirst());
+ }
+
+ @Test
+ void testBfsLevelOrder() {
+ // Create a graph where BFS should visit level by level
+ // 0
+ // /|\
+ // 1 2 3
+ // |
+ // 4
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5);
+ graph.addEdge(0, 1);
+ graph.addEdge(0, 2);
+ graph.addEdge(0, 3);
+ graph.addEdge(1, 4);
+
+ List bfs = graph.breadthFirstOrder(0);
+ assertEquals(5, bfs.size());
+ assertEquals(0, bfs.get(0));
+ // Level 1 vertices (1, 2, 3) should appear before level 2 vertex (4)
+ int indexOf4 = bfs.indexOf(4);
+ assertTrue(bfs.indexOf(1) < indexOf4);
+ assertTrue(bfs.indexOf(2) < indexOf4);
+ assertTrue(bfs.indexOf(3) < indexOf4);
+ }
+
+ @Test
+ void testDfsStartFromDifferentVertices() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4);
+ graph.addEdge(0, 1);
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+
+ // DFS from vertex 0
+ List dfs0 = graph.depthFirstOrder(0);
+ assertEquals(4, dfs0.size());
+ assertEquals(0, dfs0.get(0));
+
+ // DFS from vertex 2
+ List dfs2 = graph.depthFirstOrder(2);
+ assertEquals(4, dfs2.size());
+ assertEquals(2, dfs2.get(0));
+
+ // DFS from vertex 3
+ List dfs3 = graph.depthFirstOrder(3);
+ assertEquals(4, dfs3.size());
+ assertEquals(3, dfs3.get(0));
+ }
+
+ @Test
+ void testBfsStartFromDifferentVertices() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4);
+ graph.addEdge(0, 1);
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+
+ // BFS from vertex 0
+ List bfs0 = graph.breadthFirstOrder(0);
+ assertEquals(4, bfs0.size());
+ assertEquals(0, bfs0.get(0));
+
+ // BFS from vertex 2
+ List bfs2 = graph.breadthFirstOrder(2);
+ assertEquals(4, bfs2.size());
+ assertEquals(2, bfs2.get(0));
+ }
+
+ @Test
+ void testStarTopologyBfs() {
+ // Star graph: 0 is center connected to 1, 2, 3, 4
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5);
+ graph.addEdge(0, 1);
+ graph.addEdge(0, 2);
+ graph.addEdge(0, 3);
+ graph.addEdge(0, 4);
+
+ List bfs = graph.breadthFirstOrder(0);
+ assertEquals(5, bfs.size());
+ assertEquals(0, bfs.get(0));
+ // All neighbors should be at distance 1
+ assertTrue(bfs.containsAll(Arrays.asList(1, 2, 3, 4)));
+ }
+
+ @Test
+ void testStarTopologyDfs() {
+ // Star graph: 0 is center connected to 1, 2, 3, 4
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5);
+ graph.addEdge(0, 1);
+ graph.addEdge(0, 2);
+ graph.addEdge(0, 3);
+ graph.addEdge(0, 4);
+
+ List dfs = graph.depthFirstOrder(0);
+ assertEquals(5, dfs.size());
+ assertEquals(0, dfs.get(0));
+ assertTrue(dfs.containsAll(Arrays.asList(1, 2, 3, 4)));
+ }
+
+ @Test
+ void testNegativeStartVertexDfs() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5);
+ graph.addEdge(0, 1);
+
+ List dfs = graph.depthFirstOrder(-1);
+ assertTrue(dfs.isEmpty());
+ }
+
+ @Test
+ void testNegativeStartVertexBfs() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5);
+ graph.addEdge(0, 1);
+
+ List bfs = graph.breadthFirstOrder(-1);
+ assertTrue(bfs.isEmpty());
+ }
+
+ @Test
+ void testCompleteGraphKFour() {
+ // Complete graph K4: every vertex connected to every other vertex
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4);
+ graph.addEdge(0, 1);
+ graph.addEdge(0, 2);
+ graph.addEdge(0, 3);
+ graph.addEdge(1, 2);
+ graph.addEdge(1, 3);
+ graph.addEdge(2, 3);
+
+ assertEquals(6, graph.numberOfEdges());
+
+ List dfs = graph.depthFirstOrder(0);
+ List bfs = graph.breadthFirstOrder(0);
+
+ assertEquals(4, dfs.size());
+ assertEquals(4, bfs.size());
+ assertTrue(dfs.containsAll(Arrays.asList(0, 1, 2, 3)));
+ assertTrue(bfs.containsAll(Arrays.asList(0, 1, 2, 3)));
+ }
+
+ @Test
+ void testLargerGraphTraversal() {
+ // Create a larger graph with 10 vertices
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(10);
+ graph.addEdge(0, 1);
+ graph.addEdge(0, 2);
+ graph.addEdge(1, 3);
+ graph.addEdge(1, 4);
+ graph.addEdge(2, 5);
+ graph.addEdge(2, 6);
+ graph.addEdge(3, 7);
+ graph.addEdge(4, 8);
+ graph.addEdge(5, 9);
+
+ List dfs = graph.depthFirstOrder(0);
+ List bfs = graph.breadthFirstOrder(0);
+
+ assertEquals(10, dfs.size());
+ assertEquals(10, bfs.size());
+ assertEquals(0, dfs.get(0));
+ assertEquals(0, bfs.get(0));
+ }
+
+ @Test
+ void testSelfLoop() {
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(3);
+ graph.addEdge(0, 0); // Self loop
+ graph.addEdge(0, 1);
+ graph.addEdge(1, 2);
+
+ List dfs = graph.depthFirstOrder(0);
+ List bfs = graph.breadthFirstOrder(0);
+
+ assertEquals(3, dfs.size());
+ assertEquals(3, bfs.size());
+ }
+
+ @Test
+ void testLinearGraphTraversal() {
+ // Linear graph: 0 - 1 - 2 - 3 - 4
+ AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5);
+ graph.addEdge(0, 1);
+ graph.addEdge(1, 2);
+ graph.addEdge(2, 3);
+ graph.addEdge(3, 4);
+
+ List dfs = graph.depthFirstOrder(0);
+ List bfs = graph.breadthFirstOrder(0);
+
+ assertEquals(5, dfs.size());
+ assertEquals(5, bfs.size());
+
+ // In a linear graph, BFS and DFS starting from 0 should be the same
+ assertEquals(Arrays.asList(0, 1, 2, 3, 4), dfs);
+ assertEquals(Arrays.asList(0, 1, 2, 3, 4), bfs);
+ }
}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java
index 15e77b357f83..76b5aa8a780a 100644
--- a/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/TwoSatTest.java
@@ -8,7 +8,7 @@
/**
* Testcases for 2-SAT.
- * Please note thea whlie checking for boolean assignments always keep n + 1 elements and the first element should be always false.
+ * Please note thea while checking for boolean assignments always keep n + 1 elements and the first element should be always false.
*/
public class TwoSatTest {
private TwoSat twoSat;
diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java
new file mode 100644
index 000000000000..8d8c4e1db6bd
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/heaps/IndexedPriorityQueueTest.java
@@ -0,0 +1,350 @@
+package com.thealgorithms.datastructures.heaps;
+
+import java.util.Comparator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link IndexedPriorityQueue}.
+ *
+ * Notes:
+ * - We mainly use a Node class with a mutable "prio" field to test changeKey/decreaseKey/increaseKey.
+ * - The queue is a min-heap, so smaller "prio" means higher priority.
+ * - By default the implementation uses IdentityHashMap so duplicate-equals objects are allowed.
+ */
+public class IndexedPriorityQueueTest {
+
+ // ------------------------
+ // Helpers
+ // ------------------------
+
+ /** Simple payload with mutable priority. */
+ static class Node {
+ final String id;
+ int prio; // lower is better (min-heap)
+
+ Node(String id, int prio) {
+ this.id = id;
+ this.prio = prio;
+ }
+
+ @Override
+ public String toString() {
+ return id + "(" + prio + ")";
+ }
+ }
+
+ /** Same as Node but overrides equals/hashCode to simulate "duplicate-equals" scenario. */
+ static class NodeWithEquals {
+ final String id;
+ int prio;
+
+ NodeWithEquals(String id, int prio) {
+ this.id = id;
+ this.prio = prio;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof NodeWithEquals)) {
+ return false;
+ }
+ NodeWithEquals other = (NodeWithEquals) o;
+ // Intentionally naive equality: equal if priority is equal
+ return this.prio == other.prio;
+ }
+
+ @Override
+ public int hashCode() {
+ return Integer.hashCode(prio);
+ }
+
+ @Override
+ public String toString() {
+ return id + "(" + prio + ")";
+ }
+ }
+
+ private static IndexedPriorityQueue newNodePQ() {
+ return new IndexedPriorityQueue<>(Comparator.comparingInt(n -> n.prio));
+ }
+
+ // ------------------------
+ // Basic operations
+ // ------------------------
+
+ @Test
+ void testOfferPollWithIntegersComparableMode() {
+ // cmp == null -> elements must be Comparable
+ IndexedPriorityQueue pq = new IndexedPriorityQueue<>();
+ Assertions.assertTrue(pq.isEmpty());
+
+ pq.offer(5);
+ pq.offer(1);
+ pq.offer(3);
+
+ Assertions.assertEquals(3, pq.size());
+ Assertions.assertEquals(1, pq.peek());
+ Assertions.assertEquals(1, pq.poll());
+ Assertions.assertEquals(3, pq.poll());
+ Assertions.assertEquals(5, pq.poll());
+ Assertions.assertNull(pq.poll()); // empty -> null
+ Assertions.assertTrue(pq.isEmpty());
+ }
+
+ @Test
+ void testPeekAndIsEmpty() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Assertions.assertTrue(pq.isEmpty());
+ Assertions.assertNull(pq.peek());
+
+ pq.offer(new Node("A", 10));
+ pq.offer(new Node("B", 5));
+ pq.offer(new Node("C", 7));
+
+ Assertions.assertFalse(pq.isEmpty());
+ Assertions.assertEquals("B(5)", pq.peek().toString());
+ }
+
+ @Test
+ void testRemoveSpecificElement() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 10);
+ Node b = new Node("B", 5);
+ Node c = new Node("C", 7);
+
+ pq.offer(a);
+ pq.offer(b);
+ pq.offer(c);
+
+ // remove by reference (O(log n))
+ Assertions.assertTrue(pq.remove(b));
+ Assertions.assertEquals(2, pq.size());
+ // now min should be C(7)
+ Assertions.assertEquals("C(7)", pq.peek().toString());
+ // removing an element not present -> false
+ Assertions.assertFalse(pq.remove(b));
+ }
+
+ @Test
+ void testContainsAndClear() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 2);
+ Node b = new Node("B", 3);
+
+ pq.offer(a);
+ pq.offer(b);
+
+ Assertions.assertTrue(pq.contains(a));
+ Assertions.assertTrue(pq.contains(b));
+
+ pq.clear();
+ Assertions.assertTrue(pq.isEmpty());
+ Assertions.assertFalse(pq.contains(a));
+ Assertions.assertNull(pq.peek());
+ }
+
+ // ------------------------
+ // Key updates
+ // ------------------------
+
+ @Test
+ void testDecreaseKeyMovesUp() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 10);
+ Node b = new Node("B", 5);
+ Node c = new Node("C", 7);
+
+ pq.offer(a);
+ pq.offer(b);
+ pq.offer(c);
+
+ // current min is B(5)
+ Assertions.assertEquals("B(5)", pq.peek().toString());
+
+ // Make A more important: 10 -> 1 (smaller is better)
+ pq.decreaseKey(a, n -> n.prio = 1);
+
+ // Now A should be at the top
+ Assertions.assertEquals("A(1)", pq.peek().toString());
+ }
+
+ @Test
+ void testIncreaseKeyMovesDown() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 1);
+ Node b = new Node("B", 2);
+ Node c = new Node("C", 3);
+
+ pq.offer(a);
+ pq.offer(b);
+ pq.offer(c);
+
+ // min is A(1)
+ Assertions.assertEquals("A(1)", pq.peek().toString());
+
+ // Make A worse: 1 -> 100
+ pq.increaseKey(a, n -> n.prio = 100);
+
+ // Now min should be B(2)
+ Assertions.assertEquals("B(2)", pq.peek().toString());
+ }
+
+ @Test
+ void testChangeKeyChoosesDirectionAutomatically() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 10);
+ Node b = new Node("B", 20);
+ Node c = new Node("C", 30);
+
+ pq.offer(a);
+ pq.offer(b);
+ pq.offer(c);
+
+ // Decrease B to 0 -> should move up
+ pq.changeKey(b, n -> n.prio = 0);
+ Assertions.assertEquals("B(0)", pq.peek().toString());
+
+ // Increase B to 100 -> should move down
+ pq.changeKey(b, n -> n.prio = 100);
+ Assertions.assertEquals("A(10)", pq.peek().toString());
+ }
+
+ @Test
+ void testDirectMutationWithoutChangeKeyDoesNotReheapByDesign() {
+ // Demonstrates the contract: do NOT mutate comparator fields directly.
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 5);
+ Node b = new Node("B", 10);
+
+ pq.offer(a);
+ pq.offer(b);
+
+ // Illegally mutate priority directly
+ a.prio = 100; // worse than b now, but heap wasn't notified
+
+ // The heap structure is unchanged; peek still returns A(100) (was A(5) before)
+ // This test documents the behavior/contract rather than relying on it.
+ Assertions.assertEquals("A(100)", pq.peek().toString());
+
+ // Now fix properly via changeKey (no change in value, but triggers reheap)
+ pq.changeKey(a, n -> n.prio = n.prio);
+ Assertions.assertEquals("B(10)", pq.peek().toString());
+ }
+
+ // ------------------------
+ // Identity semantics & duplicates
+ // ------------------------
+
+ @Test
+ void testDuplicateEqualsElementsAreSupportedIdentityMap() {
+ IndexedPriorityQueue pq = new IndexedPriorityQueue<>(Comparator.comparingInt(n -> n.prio));
+
+ NodeWithEquals x1 = new NodeWithEquals("X1", 7);
+ NodeWithEquals x2 = new NodeWithEquals("X2", 7); // equals to X1 by prio, but different instance
+
+ // With IdentityHashMap internally, both can coexist
+ pq.offer(x1);
+ pq.offer(x2);
+
+ Assertions.assertEquals(2, pq.size());
+ // Poll twice; both 7s should be returned (order between x1/x2 is unspecified)
+ Assertions.assertEquals(7, pq.poll().prio);
+ Assertions.assertEquals(7, pq.poll().prio);
+ Assertions.assertTrue(pq.isEmpty());
+ }
+
+ // ------------------------
+ // Capacity growth
+ // ------------------------
+
+ @Test
+ void testGrowByManyInserts() {
+ IndexedPriorityQueue pq = new IndexedPriorityQueue<>();
+ int n = 100; // beyond default capacity (11)
+
+ for (int i = n; i >= 1; i--) {
+ pq.offer(i);
+ }
+
+ Assertions.assertEquals(n, pq.size());
+ // Ensure min-to-max order when polling
+ for (int expected = 1; expected <= n; expected++) {
+ Integer v = pq.poll();
+ Assertions.assertEquals(expected, v);
+ }
+ Assertions.assertTrue(pq.isEmpty());
+ Assertions.assertNull(pq.poll());
+ }
+
+ // ------------------------
+ // remove/contains edge cases
+ // ------------------------
+
+ @Test
+ void testRemoveHeadAndMiddleAndTail() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 1);
+ Node b = new Node("B", 2);
+ Node c = new Node("C", 3);
+ Node d = new Node("D", 4);
+
+ pq.offer(a);
+ pq.offer(b);
+ pq.offer(c);
+ pq.offer(d);
+
+ // remove head
+ Assertions.assertTrue(pq.remove(a));
+ Assertions.assertFalse(pq.contains(a));
+ Assertions.assertEquals("B(2)", pq.peek().toString());
+
+ // remove middle
+ Assertions.assertTrue(pq.remove(c));
+ Assertions.assertFalse(pq.contains(c));
+ Assertions.assertEquals("B(2)", pq.peek().toString());
+
+ // remove tail (last)
+ Assertions.assertTrue(pq.remove(d));
+ Assertions.assertFalse(pq.contains(d));
+ Assertions.assertEquals("B(2)", pq.peek().toString());
+
+ // remove last remaining
+ Assertions.assertTrue(pq.remove(b));
+ Assertions.assertTrue(pq.isEmpty());
+ Assertions.assertNull(pq.peek());
+ }
+
+ // ------------------------
+ // Error / edge cases for coverage
+ // ------------------------
+
+ @Test
+ void testInvalidInitialCapacityThrows() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new IndexedPriorityQueue(0, Comparator.naturalOrder()));
+ }
+
+ @Test
+ void testChangeKeyOnMissingElementThrows() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 10);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> pq.changeKey(a, n -> n.prio = 5));
+ }
+
+ @Test
+ void testDecreaseKeyOnMissingElementThrows() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 10);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> pq.decreaseKey(a, n -> n.prio = 5));
+ }
+
+ @Test
+ void testIncreaseKeyOnMissingElementThrows() {
+ IndexedPriorityQueue pq = newNodePQ();
+ Node a = new Node("A", 10);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> pq.increaseKey(a, n -> n.prio = 15));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java
index f80c6b5055f0..fde52b982385 100644
--- a/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java
@@ -214,10 +214,10 @@ void recursiveReverseListTest() {
@Test
void readWithEnhancedForLoopTest() {
- final var expeced = new ArrayList(Arrays.asList(10, 20, 30));
+ final var expected = new ArrayList(Arrays.asList(10, 20, 30));
SinglyLinkedList list = new SinglyLinkedList();
- for (final var x : expeced) {
+ for (final var x : expected) {
list.insert(x);
}
@@ -226,7 +226,7 @@ void readWithEnhancedForLoopTest() {
readElements.add(x);
}
- assertEquals(readElements, expeced);
+ assertEquals(readElements, expected);
}
@Test
diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java
new file mode 100644
index 000000000000..2461fd74143d
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeToStringTest.java
@@ -0,0 +1,57 @@
+package com.thealgorithms.datastructures.trees;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for the BinaryTreeToString class.
+ */
+public class BinaryTreeToStringTest {
+
+ @Test
+ public void testTreeToStringBasic() {
+ BinaryTree tree = new BinaryTree();
+ tree.put(1);
+ tree.put(2);
+ tree.put(3);
+ tree.put(4);
+
+ BinaryTreeToString converter = new BinaryTreeToString();
+ String result = converter.tree2str(tree.getRoot());
+
+ // Output will depend on insertion logic of BinaryTree.put()
+ // which is BST-style, so result = "1()(2()(3()(4)))"
+ Assertions.assertEquals("1()(2()(3()(4)))", result);
+ }
+
+ @Test
+ public void testSingleNodeTree() {
+ BinaryTree tree = new BinaryTree();
+ tree.put(10);
+
+ BinaryTreeToString converter = new BinaryTreeToString();
+ String result = converter.tree2str(tree.getRoot());
+
+ Assertions.assertEquals("10", result);
+ }
+
+ @Test
+ public void testComplexTreeStructure() {
+ BinaryTree.Node root = new BinaryTree.Node(10);
+ root.left = new BinaryTree.Node(5);
+ root.right = new BinaryTree.Node(20);
+ root.right.left = new BinaryTree.Node(15);
+ root.right.right = new BinaryTree.Node(25);
+
+ BinaryTreeToString converter = new BinaryTreeToString();
+ String result = converter.tree2str(root);
+
+ Assertions.assertEquals("10(5)(20(15)(25))", result);
+ }
+
+ @Test
+ public void testNullTree() {
+ BinaryTreeToString converter = new BinaryTreeToString();
+ Assertions.assertEquals("", converter.tree2str(null));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java
new file mode 100644
index 000000000000..43d732e54f34
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/trees/CentroidDecompositionTest.java
@@ -0,0 +1,236 @@
+package com.thealgorithms.datastructures.trees;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test cases for CentroidDecomposition
+ *
+ * @author lens161
+ */
+class CentroidDecompositionTest {
+
+ @Test
+ void testSingleNode() {
+ // Tree with just one node
+ int[][] edges = {};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(1, edges);
+
+ assertEquals(1, tree.size());
+ assertEquals(0, tree.getRoot());
+ assertEquals(-1, tree.getParent(0));
+ }
+
+ @Test
+ void testTwoNodes() {
+ // Simple tree: 0 - 1
+ int[][] edges = {{0, 1}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(2, edges);
+
+ assertEquals(2, tree.size());
+ int root = tree.getRoot();
+ assertTrue(root == 0 || root == 1, "Root should be either node 0 or 1");
+
+ // One node should be root, other should have the root as parent
+ int nonRoot = (root == 0) ? 1 : 0;
+ assertEquals(-1, tree.getParent(root));
+ assertEquals(root, tree.getParent(nonRoot));
+ }
+
+ @Test
+ void testLinearTree() {
+ // Linear tree: 0 - 1 - 2 - 3 - 4
+ int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 4}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(5, edges);
+
+ assertEquals(5, tree.size());
+ // For a linear tree of 5 nodes, the centroid should be the middle node (node 2)
+ assertEquals(2, tree.getRoot());
+ assertEquals(-1, tree.getParent(2));
+ }
+
+ @Test
+ void testBalancedBinaryTree() {
+ // Balanced binary tree:
+ // 0
+ // / \
+ // 1 2
+ // / \
+ // 3 4
+ int[][] edges = {{0, 1}, {0, 2}, {1, 3}, {1, 4}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(5, edges);
+
+ assertEquals(5, tree.size());
+ // Root should be 0 or 1 (both are valid centroids)
+ int root = tree.getRoot();
+ assertTrue(root == 0 || root == 1);
+ assertEquals(-1, tree.getParent(root));
+
+ // All nodes should have a parent in centroid tree except root
+ for (int i = 0; i < 5; i++) {
+ if (i != root) {
+ assertTrue(tree.getParent(i) >= 0 && tree.getParent(i) < 5);
+ }
+ }
+ }
+
+ @Test
+ void testStarTree() {
+ // Star tree: center node 0 connected to 1, 2, 3, 4
+ int[][] edges = {{0, 1}, {0, 2}, {0, 3}, {0, 4}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(5, edges);
+
+ assertEquals(5, tree.size());
+ // Center node (0) should be the root
+ assertEquals(0, tree.getRoot());
+
+ // All other nodes should have 0 as parent
+ for (int i = 1; i < 5; i++) {
+ assertEquals(0, tree.getParent(i));
+ }
+ }
+
+ @Test
+ void testCompleteTree() {
+ // Complete binary tree of 7 nodes:
+ // 0
+ // / \
+ // 1 2
+ // / \ / \
+ // 3 4 5 6
+ int[][] edges = {{0, 1}, {0, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(7, edges);
+
+ assertEquals(7, tree.size());
+ assertEquals(0, tree.getRoot()); // Root should be the center
+
+ // Verify all nodes are reachable in centroid tree
+ boolean[] visited = new boolean[7];
+ visited[0] = true;
+ for (int i = 1; i < 7; i++) {
+ int parent = tree.getParent(i);
+ assertTrue(parent >= 0 && parent < 7);
+ assertTrue(visited[parent], "Parent should be processed before child");
+ visited[i] = true;
+ }
+ }
+
+ @Test
+ void testLargerTree() {
+ // Tree with 10 nodes
+ int[][] edges = {{0, 1}, {0, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}, {3, 7}, {4, 8}, {5, 9}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(10, edges);
+
+ assertEquals(10, tree.size());
+ int root = tree.getRoot();
+ assertTrue(root >= 0 && root < 10);
+ assertEquals(-1, tree.getParent(root));
+
+ // Verify centroid tree structure is valid
+ for (int i = 0; i < 10; i++) {
+ if (i != root) {
+ assertTrue(tree.getParent(i) >= -1 && tree.getParent(i) < 10);
+ }
+ }
+ }
+
+ @Test
+ void testPathGraph() {
+ // Path graph with 8 nodes: 0-1-2-3-4-5-6-7
+ int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(8, edges);
+
+ assertEquals(8, tree.size());
+ // For path of 8 nodes, centroid should be around middle
+ int root = tree.getRoot();
+ assertTrue(root >= 2 && root <= 5, "Root should be near the middle of path");
+ }
+
+ @Test
+ void testInvalidEmptyTree() {
+ assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(0, new int[][] {}); });
+ }
+
+ @Test
+ void testInvalidNegativeNodes() {
+ assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(-1, new int[][] {}); });
+ }
+
+ @Test
+ void testInvalidNullEdges() {
+ assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(5, null); });
+ }
+
+ @Test
+ void testInvalidEdgeCount() {
+ // Tree with n nodes must have n-1 edges
+ int[][] edges = {{0, 1}, {1, 2}}; // 2 edges for 5 nodes (should be 4)
+ assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(5, edges); });
+ }
+
+ @Test
+ void testInvalidEdgeFormat() {
+ int[][] edges = {{0, 1, 2}}; // Edge with 3 elements instead of 2
+ assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(3, edges); });
+ }
+
+ @Test
+ void testInvalidNodeInEdge() {
+ int[][] edges = {{0, 5}}; // Node 5 doesn't exist in tree of size 3
+ assertThrows(IllegalArgumentException.class, () -> { CentroidDecomposition.buildFromEdges(3, edges); });
+ }
+
+ @Test
+ void testInvalidNodeQuery() {
+ int[][] edges = {{0, 1}, {1, 2}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(3, edges);
+
+ assertThrows(IllegalArgumentException.class, () -> { tree.getParent(-1); });
+
+ assertThrows(IllegalArgumentException.class, () -> { tree.getParent(5); });
+ }
+
+ @Test
+ void testToString() {
+ int[][] edges = {{0, 1}, {1, 2}};
+ CentroidDecomposition.CentroidTree tree = CentroidDecomposition.buildFromEdges(3, edges);
+
+ String result = tree.toString();
+ assertNotNull(result);
+ assertTrue(result.contains("Centroid Tree"));
+ assertTrue(result.contains("Node"));
+ assertTrue(result.contains("ROOT"));
+ }
+
+ @Test
+ void testAdjacencyListConstructor() {
+ List> adj = new ArrayList<>();
+ for (int i = 0; i < 3; i++) {
+ adj.add(new ArrayList<>());
+ }
+ adj.get(0).add(1);
+ adj.get(1).add(0);
+ adj.get(1).add(2);
+ adj.get(2).add(1);
+
+ CentroidDecomposition.CentroidTree tree = new CentroidDecomposition.CentroidTree(adj);
+ assertEquals(3, tree.size());
+ assertEquals(1, tree.getRoot());
+ }
+
+ @Test
+ void testNullAdjacencyList() {
+ assertThrows(IllegalArgumentException.class, () -> { new CentroidDecomposition.CentroidTree(null); });
+ }
+
+ @Test
+ void testEmptyAdjacencyList() {
+ assertThrows(IllegalArgumentException.class, () -> { new CentroidDecomposition.CentroidTree(new ArrayList<>()); });
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java
new file mode 100644
index 000000000000..c5973168438e
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/trees/ThreadedBinaryTreeTest.java
@@ -0,0 +1,50 @@
+/*
+ * TheAlgorithms (https://github.com/TheAlgorithms/Java)
+ * Author: Shewale41
+ * This file is licensed under the MIT License.
+ */
+
+package com.thealgorithms.datastructures.trees;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Basic tests for ThreadedBinaryTree inorder traversal.
+ */
+public class ThreadedBinaryTreeTest {
+
+ @Test
+ public void testInorderTraversalSimple() {
+ ThreadedBinaryTree tree = new ThreadedBinaryTree();
+ tree.insert(50);
+ tree.insert(30);
+ tree.insert(70);
+ tree.insert(20);
+ tree.insert(40);
+ tree.insert(60);
+ tree.insert(80);
+
+ List expected = List.of(20, 30, 40, 50, 60, 70, 80);
+ List actual = tree.inorderTraversal();
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testInorderWithDuplicates() {
+ ThreadedBinaryTree tree = new ThreadedBinaryTree();
+ tree.insert(5);
+ tree.insert(3);
+ tree.insert(7);
+ tree.insert(7); // duplicate
+ tree.insert(2);
+
+ List expected = List.of(2, 3, 5, 7, 7);
+ List actual = tree.inorderTraversal();
+
+ assertEquals(expected, actual);
+ }
+}
diff --git a/src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java b/src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java
new file mode 100644
index 000000000000..241f23c0fa1d
--- /dev/null
+++ b/src/test/java/com/thealgorithms/graph/GomoryHuTreeTest.java
@@ -0,0 +1,132 @@
+package com.thealgorithms.graph;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Queue;
+import java.util.Random;
+import java.util.random.RandomGenerator;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class GomoryHuTreeTest {
+
+ @Test
+ @DisplayName("Single node graph")
+ void singleNode() {
+ int[][] cap = {{0}};
+ int[][] res = GomoryHuTree.buildTree(cap);
+ int[] parent = res[0];
+ int[] weight = res[1];
+ assertEquals(-1, parent[0]);
+ assertEquals(0, weight[0]);
+ }
+
+ @Test
+ @DisplayName("Triangle undirected graph with known min-cuts")
+ void triangleGraph() {
+ // 0-1:3, 1-2:2, 0-2:4
+ int[][] cap = new int[3][3];
+ cap[0][1] = 3;
+ cap[1][0] = 3;
+ cap[1][2] = 2;
+ cap[2][1] = 2;
+ cap[0][2] = 4;
+ cap[2][0] = 4;
+
+ int[][] tree = GomoryHuTree.buildTree(cap);
+ // validate all pairs via path-min-edge equals maxflow
+ validateAllPairs(cap, tree);
+ }
+
+ @Test
+ @DisplayName("Random small undirected graphs compare to EdmondsKarp")
+ void randomSmallGraphs() {
+ Random rng = new Random(42);
+ for (int n = 2; n <= 6; n++) {
+ for (int iter = 0; iter < 10; iter++) {
+ int[][] cap = randSymmetricMatrix(n, 0, 5, rng);
+ int[][] tree = GomoryHuTree.buildTree(cap);
+ validateAllPairs(cap, tree);
+ }
+ }
+ }
+
+ private static int[][] randSymmetricMatrix(int n, int lo, int hi, RandomGenerator rng) {
+ int[][] a = new int[n][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = i + 1; j < n; j++) {
+ int w = rng.nextInt(hi - lo + 1) + lo;
+ a[i][j] = w;
+ a[j][i] = w;
+ }
+ }
+ // zero diagonal
+ for (int i = 0; i < n; i++) {
+ a[i][i] = 0;
+ }
+ return a;
+ }
+
+ private static void validateAllPairs(int[][] cap, int[][] tree) {
+ int n = cap.length;
+ int[] parent = tree[0];
+ int[] weight = tree[1];
+
+ // build adjacency list of tree without generic array creation
+ List> g = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ g.add(new ArrayList<>());
+ }
+ for (int v = 1; v < n; v++) {
+ int u = parent[v];
+ int w = weight[v];
+ g.get(u).add(new int[] {v, w});
+ g.get(v).add(new int[] {u, w});
+ }
+
+ for (int s = 0; s < n; s++) {
+ for (int t = s + 1; t < n; t++) {
+ int treeVal = minEdgeOnPath(g, s, t);
+ int flowVal = EdmondsKarp.maxFlow(cap, s, t);
+ assertEquals(flowVal, treeVal, "pair (" + s + "," + t + ")");
+ }
+ }
+ }
+
+ private static int minEdgeOnPath(List> g, int s, int t) {
+ // BFS to record parent and edge weight along the path, since it's a tree, unique path exists
+ int n = g.size();
+ int[] parent = new int[n];
+ int[] edgeW = new int[n];
+ Arrays.fill(parent, -1);
+ Queue q = new ArrayDeque<>();
+ q.add(s);
+ parent[s] = s;
+ while (!q.isEmpty()) {
+ int u = q.poll();
+ if (u == t) {
+ break;
+ }
+ for (int[] e : g.get(u)) {
+ int v = e[0];
+ int w = e[1];
+ if (parent[v] == -1) {
+ parent[v] = u;
+ edgeW[v] = w;
+ q.add(v);
+ }
+ }
+ }
+ int cur = t;
+ int ans = Integer.MAX_VALUE;
+ while (cur != s) {
+ ans = Math.min(ans, edgeW[cur]);
+ cur = parent[cur];
+ }
+ return ans == Integer.MAX_VALUE ? 0 : ans;
+ }
+}
diff --git a/src/test/java/com/thealgorithms/maths/AbundantNumberTest.java b/src/test/java/com/thealgorithms/maths/AbundantNumberTest.java
new file mode 100644
index 000000000000..5b35345afd02
--- /dev/null
+++ b/src/test/java/com/thealgorithms/maths/AbundantNumberTest.java
@@ -0,0 +1,31 @@
+package com.thealgorithms.maths;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+class AbundantNumberTest {
+ @ParameterizedTest
+ @CsvSource({"12", "66", "222", "444", "888", "2424"})
+ void abundantNumbersTest(int n) {
+ assertTrue(AbundantNumber.isAbundant(n));
+ assertTrue(AbundantNumber.isAbundantNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"1", "2", "6", "111", "333", "2222"})
+ void nonAbundantNumbersTest(int n) {
+ assertFalse(AbundantNumber.isAbundant(n));
+ assertFalse(AbundantNumber.isAbundantNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"0", "-1"})
+ void throwsNegativeNumbersNotAllowed(int n) {
+ assertThrows(IllegalArgumentException.class, () -> AbundantNumber.isAbundant(n));
+ assertThrows(IllegalArgumentException.class, () -> AbundantNumber.isAbundantNumber(n));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/maths/EvilNumberTest.java b/src/test/java/com/thealgorithms/maths/EvilNumberTest.java
new file mode 100644
index 000000000000..e59171fad25f
--- /dev/null
+++ b/src/test/java/com/thealgorithms/maths/EvilNumberTest.java
@@ -0,0 +1,28 @@
+package com.thealgorithms.maths;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+class EvilNumberTest {
+ @ParameterizedTest
+ @CsvSource({"0", "3", "10", "129", "222", "500", "777", "1198"})
+ void evilNumbersTest(int n) {
+ assertTrue(EvilNumber.isEvilNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"1", "7", "100", "333", "555", "1199"})
+ void odiousNumbersTest(int n) {
+ assertFalse(EvilNumber.isEvilNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"-1"})
+ void throwsNegativeNumbersNotAllowed(int n) {
+ assertThrows(IllegalArgumentException.class, () -> EvilNumber.isEvilNumber(n));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/maths/LuckyNumberTest.java b/src/test/java/com/thealgorithms/maths/LuckyNumberTest.java
new file mode 100644
index 000000000000..91904316b25c
--- /dev/null
+++ b/src/test/java/com/thealgorithms/maths/LuckyNumberTest.java
@@ -0,0 +1,32 @@
+package com.thealgorithms.maths;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+class LuckyNumberTest {
+
+ @ParameterizedTest
+ @CsvSource({"1", "3", "13", "49", "109", "459", "949"})
+ void luckyNumbersTest(int n) {
+ assertTrue(LuckyNumber.isLucky(n));
+ assertTrue(LuckyNumber.isLuckyNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"2", "17", "100", "300", "700"})
+ void nonLuckyNumbersTest(int n) {
+ assertFalse(LuckyNumber.isLucky(n));
+ assertFalse(LuckyNumber.isLuckyNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"0", "-1"})
+ void throwsNegativeNumbersNotAllowed(int n) {
+ assertThrows(IllegalArgumentException.class, () -> LuckyNumber.isLucky(n));
+ assertThrows(IllegalArgumentException.class, () -> LuckyNumber.isLuckyNumber(n));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/maths/PowerOfFourTest.java b/src/test/java/com/thealgorithms/maths/PowerOfFourTest.java
new file mode 100644
index 000000000000..c91f8b3cf1b5
--- /dev/null
+++ b/src/test/java/com/thealgorithms/maths/PowerOfFourTest.java
@@ -0,0 +1,36 @@
+package com.thealgorithms.maths;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class PowerOfFourTest {
+
+ @Test
+ void testPowersOfFour() {
+ assertTrue(PowerOfFour.isPowerOfFour(1));
+ assertTrue(PowerOfFour.isPowerOfFour(4));
+ assertTrue(PowerOfFour.isPowerOfFour(16));
+ assertTrue(PowerOfFour.isPowerOfFour(64));
+ assertTrue(PowerOfFour.isPowerOfFour(256));
+ assertTrue(PowerOfFour.isPowerOfFour(1024));
+ }
+
+ @Test
+ void testNonPowersOfFour() {
+ assertFalse(PowerOfFour.isPowerOfFour(2));
+ assertFalse(PowerOfFour.isPowerOfFour(3));
+ assertFalse(PowerOfFour.isPowerOfFour(5));
+ assertFalse(PowerOfFour.isPowerOfFour(8));
+ assertFalse(PowerOfFour.isPowerOfFour(15));
+ assertFalse(PowerOfFour.isPowerOfFour(32));
+ }
+
+ @Test
+ void testEdgeCases() {
+ assertFalse(PowerOfFour.isPowerOfFour(0));
+ assertFalse(PowerOfFour.isPowerOfFour(-1));
+ assertFalse(PowerOfFour.isPowerOfFour(-4));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java b/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java
index ebbd5df712fc..5d491a493ee7 100644
--- a/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java
+++ b/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java
@@ -1,46 +1,64 @@
package com.thealgorithms.maths;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+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.Arrays;
+import java.util.List;
import org.junit.jupiter.api.Test;
+/**
+ * Test cases for Sieve of Eratosthenes algorithm
+ *
+ * @author Navadeep0007
+ */
class SieveOfEratosthenesTest {
+
+ @Test
+ void testPrimesUpTo10() {
+ List expected = Arrays.asList(2, 3, 5, 7);
+ assertEquals(expected, SieveOfEratosthenes.findPrimes(10));
+ }
+
+ @Test
+ void testPrimesUpTo30() {
+ List expected = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
+ assertEquals(expected, SieveOfEratosthenes.findPrimes(30));
+ }
+
@Test
- public void testfFindPrimesTill1() {
- assertArrayEquals(new int[] {}, SieveOfEratosthenes.findPrimesTill(1));
+ void testPrimesUpTo2() {
+ List expected = Arrays.asList(2);
+ assertEquals(expected, SieveOfEratosthenes.findPrimes(2));
}
@Test
- public void testfFindPrimesTill2() {
- assertArrayEquals(new int[] {2}, SieveOfEratosthenes.findPrimesTill(2));
+ void testPrimesUpTo1() {
+ assertTrue(SieveOfEratosthenes.findPrimes(1).isEmpty());
}
@Test
- public void testfFindPrimesTill4() {
- var primesTill4 = new int[] {2, 3};
- assertArrayEquals(primesTill4, SieveOfEratosthenes.findPrimesTill(3));
- assertArrayEquals(primesTill4, SieveOfEratosthenes.findPrimesTill(4));
+ void testPrimesUpTo0() {
+ assertTrue(SieveOfEratosthenes.findPrimes(0).isEmpty());
}
@Test
- public void testfFindPrimesTill40() {
- var primesTill40 = new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
- assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(37));
- assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(38));
- assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(39));
- assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(40));
+ void testNegativeInput() {
+ assertThrows(IllegalArgumentException.class, () -> { SieveOfEratosthenes.findPrimes(-1); });
}
@Test
- public void testfFindPrimesTill240() {
- var primesTill240 = new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239};
- assertArrayEquals(primesTill240, SieveOfEratosthenes.findPrimesTill(239));
- assertArrayEquals(primesTill240, SieveOfEratosthenes.findPrimesTill(240));
+ void testCountPrimes() {
+ assertEquals(4, SieveOfEratosthenes.countPrimes(10));
+ assertEquals(25, SieveOfEratosthenes.countPrimes(100));
}
@Test
- public void testFindPrimesTillThrowsExceptionForNonPositiveInput() {
- assertThrows(IllegalArgumentException.class, () -> SieveOfEratosthenes.findPrimesTill(0));
+ void testLargeNumber() {
+ List primes = SieveOfEratosthenes.findPrimes(1000);
+ assertEquals(168, primes.size()); // There are 168 primes up to 1000
+ assertEquals(2, primes.get(0)); // First prime
+ assertEquals(997, primes.get(primes.size() - 1)); // Last prime up to 1000
}
}
diff --git a/src/test/java/com/thealgorithms/maths/SmithNumberTest.java b/src/test/java/com/thealgorithms/maths/SmithNumberTest.java
new file mode 100644
index 000000000000..4e2ba0b88e33
--- /dev/null
+++ b/src/test/java/com/thealgorithms/maths/SmithNumberTest.java
@@ -0,0 +1,22 @@
+package com.thealgorithms.maths;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+class SmithNumberTest {
+
+ @ParameterizedTest
+ @CsvSource({"4", "22", "121", "562", "985", "4937775"})
+ void positiveSmithNumbersTest(int n) {
+ assertTrue(SmithNumber.isSmithNumber(n));
+ }
+
+ @ParameterizedTest
+ @CsvSource({"2", "11", "100", "550", "999", "1234557"})
+ void negativeSmithNumbersTest(int n) {
+ assertFalse(SmithNumber.isSmithNumber(n));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java b/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java
new file mode 100644
index 000000000000..d3cc6d64bf42
--- /dev/null
+++ b/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java
@@ -0,0 +1,40 @@
+package com.thealgorithms.matrix;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class LUDecompositionTest {
+
+ @Test
+ public void testLUDecomposition() {
+ double[][] a = {{4, 3}, {6, 3}};
+
+ // Perform LU decomposition
+ LUDecomposition.LU lu = LUDecomposition.decompose(a);
+ double[][] l = lu.l;
+ double[][] u = lu.u;
+
+ // Reconstruct a from l and u
+ double[][] reconstructed = multiplyMatrices(l, u);
+
+ // Assert that reconstructed matrix matches original a
+ for (int i = 0; i < a.length; i++) {
+ assertArrayEquals(a[i], reconstructed[i], 1e-9);
+ }
+ }
+
+ // Helper method to multiply two matrices
+ private double[][] multiplyMatrices(double[][] a, double[][] b) {
+ int n = a.length;
+ double[][] c = new double[n][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ for (int k = 0; k < n; k++) {
+ c[i][j] += a[i][k] * b[k][j];
+ }
+ }
+ }
+ return c;
+ }
+}
diff --git a/src/test/java/com/thealgorithms/physics/KinematicsTest.java b/src/test/java/com/thealgorithms/physics/KinematicsTest.java
new file mode 100644
index 000000000000..c5274b0814a7
--- /dev/null
+++ b/src/test/java/com/thealgorithms/physics/KinematicsTest.java
@@ -0,0 +1,48 @@
+package com.thealgorithms.physics;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for the Kinematics utility class.
+ */
+
+public final class KinematicsTest {
+ // A small tolerance for comparing floating-point numbers
+ private static final double DELTA = 1e-9;
+ @Test
+ @DisplayName("Test final velocity: v = u + at")
+ void testCalculateFinalVelocity() {
+ assertEquals(20.0, Kinematics.calculateFinalVelocity(10.0, 2.0, 5.0), DELTA);
+ }
+
+ @Test
+ @DisplayName("Test displacement: s = ut + 0.5at^2")
+ void testCalculateDisplacement() {
+ assertEquals(75.0, Kinematics.calculateDisplacement(10.0, 2.0, 5.0), DELTA);
+ }
+
+ @Test
+ @DisplayName("Test final velocity squared: v^2 = u^2 + 2as")
+ void testCalculateFinalVelocitySquared() {
+ assertEquals(400.0, Kinematics.calculateFinalVelocitySquared(10.0, 2.0, 75.0), DELTA);
+ }
+
+ @Test
+ @DisplayName("Test displacement from average velocity: s = (u+v)/2 * t")
+ void testCalculateDisplacementFromVelocities() {
+ assertEquals(75.0, Kinematics.calculateDisplacementFromVelocities(10.0, 20.0, 5.0), DELTA);
+ }
+
+ @Test
+ @DisplayName("Test with negative acceleration (deceleration)")
+ void testDeceleration() {
+ assertEquals(10.0, Kinematics.calculateFinalVelocity(30.0, -4.0, 5.0), DELTA);
+ assertEquals(100.0, Kinematics.calculateDisplacement(30.0, -4.0, 5.0), DELTA);
+
+ assertEquals(100.0, Kinematics.calculateFinalVelocitySquared(30.0, -4.0, 100.0), DELTA);
+ assertEquals(100.0, Kinematics.calculateDisplacementFromVelocities(30.0, 10.0, 5.0), DELTA);
+ }
+}
diff --git a/src/test/java/com/thealgorithms/physics/SnellLawTest.java b/src/test/java/com/thealgorithms/physics/SnellLawTest.java
new file mode 100644
index 000000000000..ddd5fb1d5af7
--- /dev/null
+++ b/src/test/java/com/thealgorithms/physics/SnellLawTest.java
@@ -0,0 +1,41 @@
+package com.thealgorithms.physics;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class SnellLawTest {
+
+ @Test
+ public void testRefractedAngle() {
+ double n1 = 1.0; // air
+ double n2 = 1.5; // glass
+ double theta1 = Math.toRadians(30);
+
+ double theta2 = SnellLaw.refractedAngle(n1, n2, theta1);
+
+ double expected = Math.asin(n1 / n2 * Math.sin(theta1));
+
+ assertEquals(expected, theta2, 1e-12);
+ }
+
+ @Test
+ public void testTotalInternalReflection() {
+ double n1 = 1.5;
+ double n2 = 1.0;
+ double theta1 = Math.toRadians(60); // large angle
+
+ assertThrows(IllegalArgumentException.class, () -> SnellLaw.refractedAngle(n1, n2, theta1));
+ }
+
+ @Test
+ public void testNoTotalInternalReflectionAtLowAngles() {
+ double n1 = 1.5;
+ double n2 = 1.0;
+ double theta1 = Math.toRadians(10);
+
+ assertDoesNotThrow(() -> SnellLaw.refractedAngle(n1, n2, theta1));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java
deleted file mode 100644
index 7fb96dcf805f..000000000000
--- a/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.thealgorithms.puzzlesandgames;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-
-public class SudokuTest {
-
- @Test
- void testIsSafe2() {
- int[][] board = {{3, 0, 6, 5, 0, 8, 4, 0, 0}, {5, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 8, 7, 0, 0, 0, 0, 3, 1}, {0, 0, 3, 0, 1, 0, 0, 8, 0}, {9, 0, 0, 8, 6, 3, 0, 0, 5}, {0, 5, 0, 0, 9, 0, 6, 0, 0}, {1, 3, 0, 0, 0, 0, 2, 5, 0}, {0, 0, 0, 0, 0, 0, 0, 7, 4}, {0, 0, 5, 2, 0, 6, 3, 0, 0}};
-
- assertFalse(Sudoku.isSafe(board, 0, 1, 3));
- assertTrue(Sudoku.isSafe(board, 1, 2, 1));
- assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.isSafe(board, 10, 10, 5); });
- assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.isSafe(board, -1, 0, 5); });
- }
-
- @Test
- void testSolveSudoku() {
- int[][] board = {{3, 0, 6, 5, 0, 8, 4, 0, 0}, {5, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 8, 7, 0, 0, 0, 0, 3, 1}, {0, 0, 3, 0, 1, 0, 0, 8, 0}, {9, 0, 0, 8, 6, 3, 0, 0, 5}, {0, 5, 0, 0, 9, 0, 6, 0, 0}, {1, 3, 0, 0, 0, 0, 2, 5, 0}, {0, 0, 0, 0, 0, 0, 0, 7, 4}, {0, 0, 5, 2, 0, 6, 3, 0, 0}};
-
- assertTrue(Sudoku.solveSudoku(board, board.length));
- assertEquals(1, board[0][1]);
- assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.solveSudoku(board, 10); });
- assertTrue(Sudoku.solveSudoku(board, -1));
- }
-
- @Test
- void testUnsolvableSudoku() {
- int[][] unsolvableBoard = {{5, 1, 6, 8, 4, 9, 7, 3, 2}, {3, 0, 7, 6, 0, 5, 0, 0, 0}, {8, 0, 9, 7, 0, 0, 0, 6, 5}, {1, 3, 5, 0, 6, 0, 9, 0, 7}, {4, 7, 2, 5, 9, 1, 0, 0, 6}, {9, 6, 8, 3, 7, 0, 0, 5, 0}, {2, 5, 3, 1, 8, 6, 0, 7, 4}, {6, 8, 4, 2, 5, 7, 3, 9, 0}, {7, 9, 1, 4, 3, 0, 5, 0, 0}};
-
- assertFalse(Sudoku.solveSudoku(unsolvableBoard, unsolvableBoard.length));
- }
-}
diff --git a/src/test/java/com/thealgorithms/stacks/TrappingRainwaterTest.java b/src/test/java/com/thealgorithms/stacks/TrappingRainwaterTest.java
new file mode 100644
index 000000000000..909be6cd46da
--- /dev/null
+++ b/src/test/java/com/thealgorithms/stacks/TrappingRainwaterTest.java
@@ -0,0 +1,38 @@
+package com.thealgorithms.stacks;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class TrappingRainwaterTest {
+
+ @Test
+ public void testExampleCase() {
+ int[] height = {4, 2, 0, 3, 2, 5};
+ assertEquals(9, TrappingRainwater.trap(height));
+ }
+
+ @Test
+ public void testNoTrapping() {
+ int[] height = {1, 2, 3, 4, 5};
+ assertEquals(0, TrappingRainwater.trap(height));
+ }
+
+ @Test
+ public void testFlatSurface() {
+ int[] height = {0, 0, 0, 0};
+ assertEquals(0, TrappingRainwater.trap(height));
+ }
+
+ @Test
+ public void testSymmetricPit() {
+ int[] height = {3, 0, 2, 0, 3};
+ assertEquals(7, TrappingRainwater.trap(height));
+ }
+
+ @Test
+ public void testSingleBar() {
+ int[] height = {5};
+ assertEquals(0, TrappingRainwater.trap(height));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java
new file mode 100644
index 000000000000..39014780caa9
--- /dev/null
+++ b/src/test/java/com/thealgorithms/stacks/ValidParenthesesTest.java
@@ -0,0 +1,32 @@
+package com.thealgorithms.stacks;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ValidParenthesesTest {
+
+ @Test
+ void testValidParentheses() {
+ assertTrue(ValidParentheses.isValid("()"));
+ assertTrue(ValidParentheses.isValid("()[]{}"));
+ assertTrue(ValidParentheses.isValid("{[]}"));
+ assertTrue(ValidParentheses.isValid(""));
+ }
+
+ @Test
+ void testInvalidParentheses() {
+ assertFalse(ValidParentheses.isValid("(]"));
+ assertFalse(ValidParentheses.isValid("([)]"));
+ assertFalse(ValidParentheses.isValid("{{{"));
+ assertFalse(ValidParentheses.isValid("}"));
+ assertFalse(ValidParentheses.isValid("("));
+ }
+
+ @Test
+ void testNullAndOddLength() {
+ assertFalse(ValidParentheses.isValid(null));
+ assertFalse(ValidParentheses.isValid("(()"));
+ }
+}
diff --git a/src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java b/src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java
new file mode 100644
index 000000000000..df749ed9a8b5
--- /dev/null
+++ b/src/test/java/com/thealgorithms/strings/ZAlgorithmTest.java
@@ -0,0 +1,25 @@
+package com.thealgorithms.strings;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class ZAlgorithmTest {
+
+ @Test
+ void testZFunction() {
+ int[] z = ZAlgorithm.zFunction("aaaaa");
+ assertArrayEquals(new int[] {0, 4, 3, 2, 1}, z);
+ }
+
+ @Test
+ void testSearchFound() {
+ assertEquals(2, ZAlgorithm.search("abcabca", "cab"));
+ }
+
+ @Test
+ void testSearchNotFound() {
+ assertEquals(-1, ZAlgorithm.search("abcdef", "gh"));
+ }
+}