Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 42 additions & 81 deletions src/main/java/com/thealgorithms/searches/BinarySearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,39 @@
import com.thealgorithms.devutils.searches.SearchAlgorithm;

/**
* Binary Search Algorithm Implementation
* Binary Search Algorithm Implementation.
*
* <p>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.
* <p>Binary search is an efficient algorithm for finding a target element in a sorted array. It
* repeatedly divides the search range in half and compares the target value with the middle
* element.
*
* <p>IMPORTANT: This algorithm ONLY works correctly if the input array is sorted in ascending
* order.
* <p>The algorithm works as follows:
* <ol>
* <li>Start with the entire sorted array.</li>
* <li>Find the middle element of the current search range.</li>
* <li>If the middle element is equal to the target, return its index.</li>
* <li>If the target is smaller than the middle element, continue searching the left half.</li>
* <li>If the target is greater than the middle element, continue searching the right half.</li>
* <li>If the search range becomes empty, return {@code -1}.</li>
* </ol>
*
* <p>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
* <p>Example:
* <pre>
* array = [1, 3, 5, 7, 9, 11]
* key = 7
* result = 3
* </pre>
*
* <p>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
* <p>Time Complexity:
* <ul>
* <li>Best case: O(1), when the key is found at the middle index.</li>
* <li>Average case: O(log n), because the search range is divided in half each time.</li>
* <li>Worst case: O(log n), when the key is not present or is found after repeated halving.</li>
* </ul>
*
* <p>Example Walkthrough: Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] Target: 7
* <p>Space Complexity: O(log n), due to the recursive call stack.
*
* <p>Step 1: left=0, right=9, mid=4, array[4]=9 (9 &gt; 7, search left half) Step 2: left=0,
* right=3, mid=1, array[1]=3 (3 &lt; 7, search right half) Step 3: left=2, right=3, mid=2,
* array[2]=5 (5 &lt; 7, search right half) Step 4: left=3, right=3, mid=3, array[3]=7 (Found!
* Return index 3)
* <p>Note: The input array must be sorted in ascending order.
*
* @author Varun Upadhyay (https://github.com/varunu28)
* @author Podshivalov Nikita (https://github.com/nikitap492)
Expand All @@ -38,89 +45,43 @@
class BinarySearch implements SearchAlgorithm {

/**
* Generic method to perform binary search on any comparable type. This is the main entry point
* for binary search operations.
* Searches for the given key in a sorted array.
*
* <p>Example Usage:
* <pre>
* 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)
* </pre>
*
* @param <T> 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
* @param <T> the type of elements in the array; must implement {@link Comparable}
* @param array the sorted array to search in
* @param key the element to search for
* @return the index of the key if found, or {@code -1} if the key is not found
*/
@Override
public <T extends Comparable<T>> int find(T[] array, T key) {
// Handle edge case: empty array
if (array == null || array.length == 0) {
return -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.
*
* <p>How it works:
* <ol>
* <li>Calculate the middle index to avoid integer overflow</li>
* <li>Check if middle element matches the target</li>
* <li>If not, recursively search either left or right half</li>
* <li>Base case: left &gt; right means element not found</li>
* </ol>
* Recursively searches for the key between the given left and right indexes.
*
* <p>Time Complexity: O(log n) because we halve the search space each time.
* Space Complexity: O(log n) due to recursive call stack.
*
* @param <T> 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
* @param <T> the type of elements in the array; must implement {@link Comparable}
* @param array the sorted array to search in
* @param key the element to search for
* @param left the left boundary of the current search range
* @param right the right boundary of the current search range
* @return the index of the key if found, or {@code -1} if the key is not found
*/
private <T extends Comparable<T>> 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
return -1;
}

// 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 median = (left + right) >>> 1;
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
return median;
}
// 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);
}
}
Expand Down