|
3 | 3 | import com.thealgorithms.devutils.searches.SearchAlgorithm; |
4 | 4 |
|
5 | 5 | /** |
6 | | - * Binary search is one of the most popular algorithms The algorithm finds the |
7 | | - * position of a target value within a sorted array |
8 | | - * IMPORTANT |
9 | | - * This algorithm works correctly only if the input array is sorted |
| 6 | + * Binary Search Algorithm Implementation |
| 7 | + * |
| 8 | + * Binary search is one of the most efficient searching algorithms for finding a target |
| 9 | + * element in a SORTED array. It works by repeatedly dividing the search space in half, |
| 10 | + * eliminating half of the remaining elements in each step. |
| 11 | + * |
| 12 | + * IMPORTANT: This algorithm ONLY works correctly if the input array is sorted |
10 | 13 | * in ascending order. |
11 | | - * <p> |
12 | | - * Worst-case performance O(log n) Best-case performance O(1) Average |
13 | | - * performance O(log n) Worst-case space complexity O(1) |
14 | | - * |
| 14 | + * |
| 15 | + * Algorithm Overview: |
| 16 | + * 1. Start with the entire array (left = 0, right = array.length - 1) |
| 17 | + * 2. Calculate the middle index |
| 18 | + * 3. Compare the middle element with the target: |
| 19 | + * - If middle element equals target: Found! Return the index |
| 20 | + * - If middle element is less than target: Search the right half |
| 21 | + * - If middle element is greater than target: Search the left half |
| 22 | + * 4. Repeat until element is found or search space is exhausted |
| 23 | + * |
| 24 | + * Performance Analysis: |
| 25 | + * - Best-case time complexity: O(1) - Element found at middle on first try |
| 26 | + * - Average-case time complexity: O(log n) - Most common scenario |
| 27 | + * - Worst-case time complexity: O(log n) - Element not found or at extreme end |
| 28 | + * - Space complexity: O(1) - Only uses a constant amount of extra space |
| 29 | + * |
| 30 | + * Example Walkthrough: |
| 31 | + * Array: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] |
| 32 | + * Target: 7 |
| 33 | + * |
| 34 | + * Step 1: left=0, right=9, mid=4, array[4]=9 (9 > 7, search left half) |
| 35 | + * Step 2: left=0, right=3, mid=1, array[1]=3 (3 < 7, search right half) |
| 36 | + * Step 3: left=2, right=3, mid=2, array[2]=5 (5 < 7, search right half) |
| 37 | + * Step 4: left=3, right=3, mid=3, array[3]=7 (Found! Return index 3) |
| 38 | + * |
15 | 39 | * @author Varun Upadhyay (https://github.com/varunu28) |
16 | 40 | * @author Podshivalov Nikita (https://github.com/nikitap492) |
17 | 41 | * @see SearchAlgorithm |
|
20 | 44 | class BinarySearch implements SearchAlgorithm { |
21 | 45 |
|
22 | 46 | /** |
23 | | - * @param array is an array where the element should be found |
24 | | - * @param key is an element which should be found |
25 | | - * @param <T> is any comparable type |
26 | | - * @return index of the element |
| 47 | + * Generic method to perform binary search on any comparable type. |
| 48 | + * This is the main entry point for binary search operations. |
| 49 | + * |
| 50 | + * @param <T> The type of elements in the array (must be Comparable) |
| 51 | + * @param array The sorted array to search in (MUST be sorted in ascending order) |
| 52 | + * @param key The element to search for |
| 53 | + * @return The index of the key if found, -1 if not found |
| 54 | + * |
| 55 | + * @throws NullPointerException if array is null |
| 56 | + * |
| 57 | + * Example Usage: |
| 58 | + * <pre> |
| 59 | + * Integer[] numbers = {1, 3, 5, 7, 9, 11}; |
| 60 | + * int result = BinarySearch.find(numbers, 7); |
| 61 | + * // result will be 3 (index of element 7) |
| 62 | + * |
| 63 | + * int notFound = BinarySearch.find(numbers, 4); |
| 64 | + * // notFound will be -1 (element 4 does not exist) |
| 65 | + * </pre> |
27 | 66 | */ |
28 | 67 | @Override |
29 | 68 | public <T extends Comparable<T>> int find(T[] array, T key) { |
| 69 | + // Handle edge case: empty array |
30 | 70 | if (array == null || array.length == 0) { |
31 | 71 | return -1; |
32 | 72 | } |
| 73 | + |
| 74 | + // Delegate to the core search implementation |
33 | 75 | return search(array, key, 0, array.length - 1); |
34 | 76 | } |
35 | 77 |
|
36 | 78 | /** |
37 | | - * This method implements the Generic Binary Search |
38 | | - * |
39 | | - * @param array The array to make the binary search |
40 | | - * @param key The number you are looking for |
41 | | - * @param left The lower bound |
42 | | - * @param right The upper bound |
43 | | - * @return the location of the key |
| 79 | + * Core recursive implementation of binary search algorithm. |
| 80 | + * This method divides the problem into smaller subproblems recursively. |
| 81 | + * |
| 82 | + * How it works: |
| 83 | + * 1. Calculate the middle index to avoid integer overflow |
| 84 | + * 2. Check if middle element matches the target |
| 85 | + * 3. If not, recursively search either left or right half |
| 86 | + * 4. Base case: left > right means element not found |
| 87 | + * |
| 88 | + * @param <T> The type of elements (must be Comparable) |
| 89 | + * @param array The sorted array to search in |
| 90 | + * @param key The element we're looking for |
| 91 | + * @param left The leftmost index of current search range (inclusive) |
| 92 | + * @param right The rightmost index of current search range (inclusive) |
| 93 | + * @return The index where key is located, or -1 if not found |
| 94 | + * |
| 95 | + * Time Complexity: O(log n) because we halve the search space each time |
| 96 | + * Space Complexity: O(log n) due to recursive call stack |
44 | 97 | */ |
45 | 98 | private <T extends Comparable<T>> int search(T[] array, T key, int left, int right) { |
| 99 | + // Base case: Search space is exhausted |
| 100 | + // This happens when left pointer crosses right pointer |
46 | 101 | if (right < left) { |
47 | | - return -1; // this means that the key not found |
| 102 | + return -1; // Key not found in the array |
48 | 103 | } |
49 | | - // find median |
50 | | - int median = (left + right) >>> 1; |
| 104 | + |
| 105 | + // Calculate middle index |
| 106 | + // Using (left + right) / 2 could cause integer overflow for large arrays |
| 107 | + // So we use: left + (right - left) / 2 which is mathematically equivalent |
| 108 | + // but prevents overflow |
| 109 | + int median = (left + right) >>> 1; // Unsigned right shift is faster division by 2 |
| 110 | + |
| 111 | + // Get the value at middle position for comparison |
51 | 112 | int comp = key.compareTo(array[median]); |
52 | 113 |
|
| 114 | + // Case 1: Found the target element at middle position |
53 | 115 | if (comp == 0) { |
54 | | - return median; |
55 | | - } else if (comp < 0) { |
| 116 | + return median; // Return the index where element was found |
| 117 | + } |
| 118 | + // Case 2: Target is smaller than middle element |
| 119 | + // This means if target exists, it must be in the LEFT half |
| 120 | + else if (comp < 0) { |
| 121 | + // Recursively search the left half |
| 122 | + // New search range: [left, median - 1] |
56 | 123 | return search(array, key, left, median - 1); |
57 | | - } else { |
| 124 | + } |
| 125 | + // Case 3: Target is greater than middle element |
| 126 | + // This means if target exists, it must be in the RIGHT half |
| 127 | + else { |
| 128 | + // Recursively search the right half |
| 129 | + // New search range: [median + 1, right] |
58 | 130 | return search(array, key, median + 1, right); |
59 | 131 | } |
60 | 132 | } |
|
0 commit comments