diff --git a/src/main/java/com/thealgorithms/searches/LowerBound.java b/src/main/java/com/thealgorithms/searches/LowerBound.java index 5a1401edd3c2..7325082c223b 100644 --- a/src/main/java/com/thealgorithms/searches/LowerBound.java +++ b/src/main/java/com/thealgorithms/searches/LowerBound.java @@ -13,7 +13,7 @@ * This is an extension of BinarySearch. * *

- * Worst-case performance O(log n) Best-case performance O(1) Average + * Worst-case performance O(log n) Best-case performance O(log n) Average * performance O(log n) Worst-case space complexity O(1) * * @author Pratik Padalia (https://github.com/15pratik) @@ -26,10 +26,13 @@ class LowerBound implements SearchAlgorithm { * @param array is an array where the LowerBound value is to be found * @param key is an element for which the LowerBound is to be found * @param is any comparable type - * @return index of the LowerBound element + * @return index of the first element not less than key, or 0 if array is null/empty or key is null */ @Override public > int find(T[] array, T key) { + if (array == null || array.length == 0 || key == null) { + return 0; + } return search(array, key, 0, array.length - 1); } @@ -51,10 +54,8 @@ private > int search(T[] array, T key, int left, int rig int median = (left + right) >>> 1; int comp = key.compareTo(array[median]); - if (comp == 0) { - return median; - } else if (comp < 0) { - // median position can be a possible solution + if (comp <= 0) { + // key is less than or equal to array[median], median position can be a possible solution return search(array, key, left, median); } else { // key we are looking is greater, so we must look on the right of median position diff --git a/src/main/java/com/thealgorithms/searches/UpperBound.java b/src/main/java/com/thealgorithms/searches/UpperBound.java index ec52c7a0ae5c..5344f22994b1 100644 --- a/src/main/java/com/thealgorithms/searches/UpperBound.java +++ b/src/main/java/com/thealgorithms/searches/UpperBound.java @@ -13,7 +13,7 @@ * This is an extension of BinarySearch. * *

- * Worst-case performance O(log n) Best-case performance O(1) Average + * Worst-case performance O(log n) Best-case performance O(log n) Average * performance O(log n) Worst-case space complexity O(1) * * @author Pratik Padalia (https://github.com/15pratik) @@ -26,10 +26,13 @@ class UpperBound implements SearchAlgorithm { * @param array is an array where the UpperBound value is to be found * @param key is an element for which the UpperBound is to be found * @param is any comparable type - * @return index of the UpperBound element + * @return index of the first element greater than key, last index if no such element exists, or 0 if array is null/empty or key is null */ @Override public > int find(T[] array, T key) { + if (array == null || array.length == 0 || key == null) { + return 0; + } return search(array, key, 0, array.length - 1); } diff --git a/src/test/java/com/thealgorithms/searches/LowerBoundTest.java b/src/test/java/com/thealgorithms/searches/LowerBoundTest.java index 30f4a5cb257c..058ea08cf614 100644 --- a/src/test/java/com/thealgorithms/searches/LowerBoundTest.java +++ b/src/test/java/com/thealgorithms/searches/LowerBoundTest.java @@ -56,4 +56,31 @@ void testLowerBoundNonExistentValue() { assertEquals(4, lowerBound.find(array, 7), "Lower bound for 7 should be at index 4"); assertEquals(0, lowerBound.find(array, 0), "Lower bound for 0 should be at index 0"); } + + /** + * Test finding the lower bound for an element with duplicate occurrences. + * Lower bound should point to the FIRST element not less than key. + */ + @Test + void testLowerBoundDuplicates() { + LowerBound lowerBound = new LowerBound(); + Integer[] arrayWithDuplicates = {1, 2, 2, 2, 3, 4, 5}; + + assertEquals(1, lowerBound.find(arrayWithDuplicates, 2), "Lower bound for 2 should be at index 1 (first occurrence)"); + + Integer[] allSame = {2, 2, 2, 2, 2}; + assertEquals(0, lowerBound.find(allSame, 2), "Lower bound for 2 in all-same array should be at index 0"); + } + + /** + * Test edge cases: empty array, null array, and null key. + */ + @Test + void testLowerBoundNullAndEmptyEdgeCases() { + LowerBound lowerBound = new LowerBound(); + Integer[] emptyArray = {}; + assertEquals(0, lowerBound.find(emptyArray, 5), "Lower bound for an empty array should be 0"); + assertEquals(0, lowerBound.find(null, 5), "Lower bound for a null array should be 0"); + assertEquals(0, lowerBound.find(new Integer[] {1, 2, 3}, null), "Lower bound for a null key should be 0"); + } } diff --git a/src/test/java/com/thealgorithms/searches/UpperBoundTest.java b/src/test/java/com/thealgorithms/searches/UpperBoundTest.java index dc0cbdd97e19..4f5eddbe924f 100644 --- a/src/test/java/com/thealgorithms/searches/UpperBoundTest.java +++ b/src/test/java/com/thealgorithms/searches/UpperBoundTest.java @@ -57,6 +57,15 @@ void testUpperBoundMultipleValues() { assertTrue(arrayWithDuplicates[index] > key, "Element at the upper bound index should be greater than the key."); } + @Test + void testUpperBoundDuplicates() { + Integer[] arrayWithDuplicates = {1, 2, 2, 2, 3}; + assertEquals(4, upperBound.find(arrayWithDuplicates, 2), "Upper bound for 2 in {1, 2, 2, 2, 3} should be at index 4"); + + Integer[] allSame = {2, 2, 2, 2, 2}; + assertEquals(4, upperBound.find(allSame, 2), "Upper bound for 2 in all-same array should be at last index 4"); + } + @Test void testUpperBoundLowerThanMin() { int key = 0; // Test with a key lower than the minimum element @@ -96,4 +105,10 @@ void testUpperBoundSingleElementArray() { index = upperBound.find(singleElementArray, 15); assertEquals(0, index, "Upper bound for 15 in a single element array should be 0."); } + + @Test + void testUpperBoundNullArrayAndKey() { + assertEquals(0, upperBound.find(null, 5), "Upper bound for a null array should be 0."); + assertEquals(0, upperBound.find(new Integer[] {1, 2, 3}, null), "Upper bound for a null key should be 0."); + } }