diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch.java b/src/main/java/com/thealgorithms/searches/BinarySearch.java index ca873fc6eafa..11d2d065536a 100644 --- a/src/main/java/com/thealgorithms/searches/BinarySearch.java +++ b/src/main/java/com/thealgorithms/searches/BinarySearch.java @@ -5,30 +5,38 @@ /** * Binary Search Algorithm Implementation * - *

Binary search is one of the most efficient searching algorithms for finding a target element - * in a SORTED array. It works by repeatedly dividing the search space in half, eliminating half of - * the remaining elements in each step. + *

+ * Binary search is one of the most efficient searching algorithms for finding a + * target element in a SORTED array. It works by repeatedly dividing the search + * space in half, eliminating half of the remaining elements in each step. * - *

IMPORTANT: This algorithm ONLY works correctly if the input array is sorted in ascending - * order. + *

+ * IMPORTANT: This algorithm ONLY works correctly if the input array is sorted + * in ascending order. * - *

Algorithm Overview: 1. Start with the entire array (left = 0, right = array.length - 1) 2. - * Calculate the middle index 3. Compare the middle element with the target: - If middle element - * equals target: Found! Return the index - If middle element is less than target: Search the right - * half - If middle element is greater than target: Search the left half 4. Repeat until element is - * found or search space is exhausted + *

+ * Algorithm Overview: 1. Start with the entire array (left = 0, right = + * array.length - 1) 2. Calculate the middle index 3. Compare the middle element + * with the target: - If middle element equals target: Found! Return the index - + * If middle element is less than target: Search the right half - If middle + * element is greater than target: Search the left half 4. Repeat until element + * is found or search space is exhausted * - *

Performance Analysis: - Best-case time complexity: O(1) - Element found at middle on first - * try - Average-case time complexity: O(log n) - Most common scenario - Worst-case time - * complexity: O(log n) - Element not found or at extreme end - Space complexity: O(1) - Only uses - * a constant amount of extra space + *

+ * Performance Analysis: - Best-case time complexity: O(1) - Element found at + * middle on first try - Average-case time complexity: O(log n) - Most common + * scenario - Worst-case time complexity: O(log n) - Element not found or at + * extreme end - Space complexity: O(1) - Only uses a constant amount of extra + * space * - *

Example Walkthrough: Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] Target: 7 + *

+ * Example Walkthrough: Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] Target: 7 * - *

Step 1: left=0, right=9, mid=4, array[4]=9 (9 > 7, search left half) Step 2: left=0, - * right=3, mid=1, array[1]=3 (3 < 7, search right half) Step 3: left=2, right=3, mid=2, - * array[2]=5 (5 < 7, search right half) Step 4: left=3, right=3, mid=3, array[3]=7 (Found! - * Return index 3) + *

+ * Step 1: left=0, right=9, mid=4, array[4]=9 (9 > 7, search left half) Step + * 2: left=0, right=3, mid=1, array[1]=3 (3 < 7, search right half) Step 3: + * left=2, right=3, mid=2, array[2]=5 (5 < 7, search right half) Step 4: + * left=3, right=3, mid=3, array[3]=7 (Found! Return index 3) * * @author Varun Upadhyay (https://github.com/varunu28) * @author Podshivalov Nikita (https://github.com/nikitap492) @@ -37,98 +45,114 @@ */ class BinarySearch implements SearchAlgorithm { - /** - * Generic method to perform binary search on any comparable type. This is the main entry point - * for binary search operations. - * - *

Example Usage: - *

-     * Integer[] numbers = {1, 3, 5, 7, 9, 11};
-     * int result = new BinarySearch().find(numbers, 7);
-     * // result will be 3 (index of element 7)
-     *
-     * int notFound = new BinarySearch().find(numbers, 4);
-     * // notFound will be -1 (element 4 does not exist)
-     * 
- * - * @param The type of elements in the array (must be Comparable) - * @param array The sorted array to search in (MUST be sorted in ascending order) - * @param key The element to search for - * @return The index of the key if found, -1 if not found or if array is null/empty - */ - @Override - public > int find(T[] array, T key) { - // Handle edge case: null or empty array - if (array == null || array.length == 0) { - return -1; - } + /** + * Generic method to perform binary search on any comparable type. This is the + * main entry point for binary search operations. + * + *

+ * Example Usage: + * + *

+	 * Integer[] numbers = { 1, 3, 5, 7, 9, 11 };
+	 * int result = new BinarySearch().find(numbers, 7);
+	 * // result will be 3 (index of element 7)
+	 *
+	 * int notFound = new BinarySearch().find(numbers, 4);
+	 * // notFound will be -1 (element 4 does not exist)
+	 * 
+ * + * @param The type of elements in the array (must be Comparable) + * @param array The sorted array to search in (MUST be sorted in ascending + * order) + * @param key The element to search for + * @return The index of the key if found, -1 if not found or if array is + * null/empty + * + *

Edge Cases: + *

    + *
  • Null array → returns -1
  • + *
  • Empty array → returns -1
  • + *
  • Null key → returns -1
  • + *
  • Element not found → returns -1
  • + *
  • Single element array → works correctly
  • + *
  • Duplicate elements → may return any one valid index
  • + *
+ */ + @Override + public > int find(T[] array, T key) { + // Handle edge case: null or empty array + if (array == null || array.length == 0) { + return -1; + } - // Handle edge case: null key - // Searching for null in an array of Comparables is undefined behavior - // Return -1 to indicate not found rather than throwing NPE - if (key == null) { - return -1; - } + // Handle edge case: null key + // Searching for null in an array of Comparables is undefined behavior + // Return -1 to indicate not found rather than throwing NPE + if (key == null) { + return -1; + } - // Delegate to the core search implementation - return search(array, key, 0, array.length - 1); - } + // Delegate to the core search implementation + return search(array, key, 0, array.length - 1); + } - /** - * Core recursive implementation of binary search algorithm. This method divides the problem - * into smaller subproblems recursively. - * - *

How it works: - *

    - *
  1. Calculate the middle index to avoid integer overflow
  2. - *
  3. Check if middle element matches the target
  4. - *
  5. If not, recursively search either left or right half
  6. - *
  7. Base case: left > right means element not found
  8. - *
- * - *

Time Complexity: O(log n) because we halve the search space each time. - * Space Complexity: O(log n) due to recursive call stack. - * - * @param The type of elements (must be Comparable) - * @param array The sorted array to search in - * @param key The element we're looking for - * @param left The leftmost index of current search range (inclusive) - * @param right The rightmost index of current search range (inclusive) - * @return The index where key is located, or -1 if not found - */ - private > int search(T[] array, T key, int left, int right) { - // Base case: Search space is exhausted - // This happens when left pointer crosses right pointer - if (right < left) { - return -1; // Key not found in the array - } + /** + * Core recursive implementation of binary search algorithm. This method divides + * the problem into smaller subproblems recursively. + * + *

+ * How it works: + *

    + *
  1. Calculate the middle index to avoid integer overflow
  2. + *
  3. Check if middle element matches the target
  4. + *
  5. If not, recursively search either left or right half
  6. + *
  7. Base case: left > right means element not found
  8. + *
+ * + *

+ * Time Complexity: O(log n) because we halve the search space each time. Space + * Complexity: O(log n) due to recursive call stack. + * + * @param The type of elements (must be Comparable) + * @param array The sorted array to search in + * @param key The element we're looking for + * @param left The leftmost index of current search range (inclusive) + * @param right The rightmost index of current search range (inclusive) + * @return The index where key is located, or -1 if not found + */ + private > int search(T[] array, T key, int left, int right) { + // Base case: Search space is exhausted + // This happens when left pointer crosses right pointer + if (right < left) { + return -1; // Key not found in the array + } - // Calculate middle index - // Using (left + right) / 2 could cause integer overflow for large arrays - // So we use: left + (right - left) / 2 which is mathematically equivalent - // but prevents overflow - int median = (left + right) >>> 1; // Unsigned right shift is faster division by 2 + // Calculate middle index + // Using (left + right) / 2 could cause integer overflow for large arrays + // So we use: left + (right - left) / 2 which is mathematically equivalent + // but prevents overflow + int median = (left + right) >>> 1; // Unsigned right shift is faster division by 2 - // Get the value at middle position for comparison - int comp = key.compareTo(array[median]); + // Get the value at middle position for comparison + int comp = key.compareTo(array[median]); - // Case 1: Found the target element at middle position - if (comp == 0) { - return median; // Return the index where element was found - } - // Case 2: Target is smaller than middle element - // This means if target exists, it must be in the LEFT half - else if (comp < 0) { - // Recursively search the left half - // New search range: [left, median - 1] - return search(array, key, left, median - 1); - } - // Case 3: Target is greater than middle element - // This means if target exists, it must be in the RIGHT half - else { - // Recursively search the right half - // New search range: [median + 1, right] - return search(array, key, median + 1, right); - } - } + // Case 1: Found the target element at middle position + if (comp == 0) { + return median; // Return the index where element was found + } + // Case 2: Target is smaller than middle element + // This means if target exists, it must be in the LEFT half + else if (comp < 0) { + // Recursively search the left half + // New search range: [left, median - 1] + return search(array, key, left, median - 1); + } + // Case 3: Target is greater than middle element + // This means if target exists, it must be in the RIGHT half + else { + // Recursively search the right half + // New search range: [median + 1, right] + return search(array, key, median + 1, right); + } + } } diff --git a/src/main/java/com/thealgorithms/searches/JumpSearch.java b/src/main/java/com/thealgorithms/searches/JumpSearch.java index 5074aa7845c8..e93738f1cb28 100644 --- a/src/main/java/com/thealgorithms/searches/JumpSearch.java +++ b/src/main/java/com/thealgorithms/searches/JumpSearch.java @@ -27,6 +27,17 @@ * Result: Index = 4 * *

+ * Example (Simple):
+ * Input: arr = [2, 4, 6, 8, 10], key = 8
+ * Output: 3 + * + *

+ * Explanation (Easy):
+ * Instead of checking every element one by one, Jump Search skips elements + * by jumping ahead fixed steps (√n). Once it finds a range where the element + * might exist, it performs a linear search in that smaller block. + * + *

* Time Complexity:
* - Best-case: O(1) - element found at first position
* - Average: O(√n) - optimal block size reduces jumps
diff --git a/src/main/java/com/thealgorithms/stacks/StackUsingLinkedList.java b/src/main/java/com/thealgorithms/stacks/StackUsingLinkedList.java new file mode 100644 index 000000000000..88197231795c --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/StackUsingLinkedList.java @@ -0,0 +1,70 @@ +package com.thealgorithms.stacks; + +/** + * A class that implements a Stack using a singly linked list. + * Supports basic operations like push, pop, peek, and isEmpty. + * + * Reference: https://www.geeksforgeeks.org/stack-using-linked-list/ + */ +public class StackUsingLinkedList { + + /** + * Node class representing each element in the stack + */ + private static class Node { + int data; + Node next; + + Node(int data) { + this.data = data; + } + } + + private Node top; + + /** + * Push an element onto the stack + * + * @param value the value to push + */ + public void push(int value) { + Node newNode = new Node(value); + newNode.next = top; + top = newNode; + } + + /** + * Remove and return the top element of the stack + * + * @return top element + */ + public int pop() { + if (top == null) { + throw new RuntimeException("Stack is empty"); + } + int value = top.data; + top = top.next; + return value; + } + + /** + * Return the top element without removing it + * + * @return top element + */ + public int peek() { + if (top == null) { + throw new RuntimeException("Stack is empty"); + } + return top.data; + } + + /** + * Check if the stack is empty + * + * @return true if empty, false otherwise + */ + public boolean isEmpty() { + return top == null; + } +} diff --git a/src/test/java/com/thealgorithms/stacks/StackUsingLinkedListTest.java b/src/test/java/com/thealgorithms/stacks/StackUsingLinkedListTest.java new file mode 100644 index 000000000000..c06604fa95fd --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/StackUsingLinkedListTest.java @@ -0,0 +1,75 @@ +package com.thealgorithms.stacks; + +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; + +/** + * Test class for StackUsingLinkedList. + * + * This class contains unit tests to verify the correctness + * of stack operations such as push, pop, peek, and isEmpty. + * + * Reference: https://www.geeksforgeeks.org/stack-using-linked-list/ + */ +class StackUsingLinkedListTest { + + /** + * Test push and pop operations + */ + @Test + void testPushAndPop() { + StackUsingLinkedList stack = new StackUsingLinkedList(); + stack.push(10); + stack.push(20); + + assertEquals(20, stack.pop()); + assertEquals(10, stack.pop()); + } + + /** + * Test peek operation + */ + @Test + void testPeek() { + StackUsingLinkedList stack = new StackUsingLinkedList(); + stack.push(5); + + assertEquals(5, stack.peek()); + } + + /** + * Test isEmpty method + */ + @Test + void testIsEmpty() { + StackUsingLinkedList stack = new StackUsingLinkedList(); + + assertTrue(stack.isEmpty()); + stack.push(1); + assertFalse(stack.isEmpty()); + } + + /** + * Test pop on empty stack (edge case) + */ + @Test + void testPopOnEmptyStack() { + StackUsingLinkedList stack = new StackUsingLinkedList(); + + assertThrows(RuntimeException.class, stack::pop); + } + + /** + * Test peek on empty stack (edge case) + */ + @Test + void testPeekOnEmptyStack() { + StackUsingLinkedList stack = new StackUsingLinkedList(); + + assertThrows(RuntimeException.class, stack::peek); + } +}