Class BinarySearch
For sorted lists and arrays:
BinarySearch.inSortedArray([10, 20, 30, 40]).find(20)
=> Optional.of(1)
// Find the insertion point if not found
BinarySearch.inSortedList([10, 20, 30, 40]).insertionPointFor(22)
=> InsertionPoint.before(2)
// Search for double with a tolerance factor
// And find the range of all matches
BinarySearch.inSortedArrayWithTolerance([1.1, 2.1, 2.2, 2.3, 3.3, 4.4], 0.5).rangeOf(2)
=> Range.closed(1, 3)
To solve a monotonic polynomial function:
double polynomial(double x) {
return pow(x, 3) + 3 * x;
}
InsertionPoint<Double> solve(double y) {
return BinarySearch.forDoubles()
.insertionPointFor((low, mid, high) -> Double.compare(y, polynomial(mid)));
}
To find the minimum int value of a parabola:
double parabola(int x) {
return pow(x, 2) + 4 * x - 3;
}
int minimum = BinarySearch.forInts()
.insertionPointFor(
(low, mid, high) -> Double.compare(parabola(mid - 1), parabola(mid)))
.floor();
=> -2
To emulate the Guess The Number game:
BinarySearch.forInts()
.find((lo, mid, hi) -> tooHighOrTooLow(mid))
=> Optional.of(theGuessedNumber)
The forInts(), forLongs(), forDoubles() and the primitive array search
methods perform no boxing in the O(logn) search operation.
Note that except inSortedList(List, Comparator), which may support null search
targets if the comparator supports nulls, no other BinarySearch.Table implementations
allow null search targets.
- Since:
- 8.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceRepresents the search target that can be found through bisecting the double domain.static interfaceRepresents the search target that can be found through bisecting the integer domain.static interfaceRepresents the search target that can be found through bisecting the long integer domain.static classBinarySearch.Table<K, C extends Comparable<C>>Like a hash table, allows looking up comparable values by a key, except the lookup is through binary search instead of hashing. -
Method Summary
Modifier and TypeMethodDescriptionReturns aBinarySearch.Tableover all finite double values (exceptDouble.NaN,Double.NEGATIVE_INFINITYandDouble.POSITIVE_INFINITY).forDoubles(Range<Double> range) Similar toforInts(Range), but returns aBinarySearch.Tableover the givenrangeofdoubleprecision numbers.forInts()Returns aBinarySearch.Tableover all integers.Returns aBinarySearch.Tableover the givenrange.forLongs()static BinarySearch.Table<Integer, Integer> inSortedArray(int[] array) Returns aBinarySearch.Tableto search for array element indexes in the given sorted intarray.static BinarySearch.Table<Long, Integer> inSortedArray(long[] array) Returns aBinarySearch.Tableto search for array element indexes in the given sorted longarray.static BinarySearch.Table<Double, Integer> inSortedArrayWithTolerance(double[] array, double tolerance) Returns aBinarySearch.Tableto search for array element indexes in the given sorted doublearray.static <E extends Comparable<E>>
BinarySearch.Table<E, Integer> inSortedList(List<? extends E> list) Returns aBinarySearch.Tableto search for element indexes in the given sortedlist.static <E> BinarySearch.Table<E, Integer> inSortedList(List<? extends E> list, Comparator<? super E> sortedBy) Returns aBinarySearch.Tableto search for element indexes in the given sortedlistaccording tocomparator.static <Q extends Comparable<Q>, E>
BinarySearch.Table<Q, Integer> inSortedList(List<? extends E> list, Function<? super E, ? extends Q> sortedBy) Returns aBinarySearch.Tableto search for element indexes in the givenlistsorted by thesortByfunction.static BinarySearch.Table<Double, Integer> inSortedListWithTolerance(List<Double> list, double tolerance) Returns aBinarySearch.Tableto search for element indexes in the given list of sorteddoublevalues.
-
Method Details
-
inSortedList
public static <E extends Comparable<E>> BinarySearch.Table<E,Integer> inSortedList(List<? extends E> list) Returns aBinarySearch.Tableto search for element indexes in the given sortedlist.For example:
inSortedList(numbers).find(20).This is an O(1) operation.
- Parameters:
list- expected to support random access (notLinkedListfor instance), or else performance will suffer.
-
inSortedList
public static <E> BinarySearch.Table<E,Integer> inSortedList(List<? extends E> list, Comparator<? super E> sortedBy) Returns aBinarySearch.Tableto search for element indexes in the given sortedlistaccording tocomparator.For example:
inSortedList(timestamps, nullsFirst(naturalOrder())).find(timestamp).This is an O(1) operation.
- Parameters:
list- expected to support random access (notLinkedListfor instance), or else performance will suffer.
-
inSortedList
public static <Q extends Comparable<Q>, E> BinarySearch.Table<Q,Integer> inSortedList(List<? extends E> list, Function<? super E, ? extends Q> sortedBy) Returns aBinarySearch.Tableto search for element indexes in the givenlistsorted by thesortByfunction.For example:
inSortedList(employees, Employee::age).rangeOf(20).This is an O(1) operation.
- Parameters:
list- expected to support random access (notLinkedListfor instance), or else performance will suffer.
-
inSortedArray
Returns aBinarySearch.Tableto search for array element indexes in the given sorted intarray.For example:
inSortedArray(numbers).find(20).This is an O(1) operation.
-
inSortedArray
Returns aBinarySearch.Tableto search for array element indexes in the given sorted longarray.For example:
inSortedArray(largeNumbers).find(1000000000000L).This is an O(1) operation.
-
inSortedListWithTolerance
public static BinarySearch.Table<Double,Integer> inSortedListWithTolerance(List<Double> list, double tolerance) Returns aBinarySearch.Tableto search for element indexes in the given list of sorteddoublevalues. The positivetoleranceis respected when comparing double values.For example:
inSortedListWithTolerance(temperatures, 0.1).find(30).This is an O(1) operation.
- Parameters:
list- expected to support random access (notLinkedListfor instance), or else performance will suffer.tolerance- an inclusive upper bound on the difference between the search target and elements in the list, which must be a non-negative finite value, i.e. notDouble.NaN,Double.POSITIVE_INFINITY, or negative, including-0.0
-
inSortedArrayWithTolerance
public static BinarySearch.Table<Double,Integer> inSortedArrayWithTolerance(double[] array, double tolerance) Returns aBinarySearch.Tableto search for array element indexes in the given sorted doublearray. The positivetoleranceis respected when comparing double values.For example:
inSortedArrayWithTolerance(temperatures, 0.1).find(30).This is an O(1) operation.
-
forInts
Returns aBinarySearch.Tableover all integers.Callers can search by an
BinarySearch.IntSearchTargetobject that will be called at each probe to determine whether the target is already found at the current mid-point, to the left half of the current subrange, or to the right half of the current subrange.This is an O(1) operation.
-
forInts
public static BinarySearch.Table<BinarySearch.IntSearchTarget, Integer> forInts(Range<Integer> range) Returns aBinarySearch.Tableover the givenrange.Callers can search by an
BinarySearch.IntSearchTargetobject that will be called at each probe to determine whether the target is already found at the current mid-point, to the left half of the current subrange, or to the right half of the current subrange.While the common use cases of binary search is to search in sorted arrays and lists, there are diverse contexts where the algorithm is also applicable (think of the Guess the Number game). As a more realistic example, you can binary search a rotated, otherwise strictly-ordered array, using the following code:
Optional<Integer> binarySearchRotated(int[] rotated, int target) { return BinarySearch.forInts(Range.closedOpen(0, rotated.length)) find((low, mid, high) -> { int probe = rotated[mid]; if (target < probe) { return rotated[low] <= probe && target < rotated[low] ? 1 : -1; } else if (target > probe) { return probe <= rotated[high] && target > rotated[high] ? -1 : 1; } else { return 0; } }); }This is an O(1) operation.
-
forLongs
Similar toforInts(), but returns aBinarySearch.Tableover alllongintegers.Callers can search by a
BinarySearch.LongSearchTargetobject that will be called at each probe to determine whether the target is already found at the current mid-point, to the left half of the current subrange, or to the right half of the current subrange.For example, if we are to emulate the "Guess The Number" game, there will be a secret number and we can bisect the full long integer domain to find the number as in:
Optional<Long> guessed = forLongs().find((low, mid, high) -> Integer.compare(secret, mid)); assertThat(guessed).hasValue(secret);This is an O(1) operation.
-
forLongs
Similar toforInts(Range), but returns aBinarySearch.Tableover the givenrangeoflongintegers.Callers can search by a
BinarySearch.LongSearchTargetobject that will be called at each probe to determine whether the target is already found at the current mid-point, to the left half of the current subrange, or to the right half of the current subrange.This is an O(1) operation.
-
forDoubles
Returns aBinarySearch.Tableover all finite double values (exceptDouble.NaN,Double.NEGATIVE_INFINITYandDouble.POSITIVE_INFINITY).Callers can search by an
BinarySearch.DoubleSearchTargetobject that will be called at each probe to determine whether the target is already found at the current mid-point, to the left half of the current subrange, or to the right half of the current subrange.Different from
inSortedArrayWithTolerance(double[], double), which is to search within the int indexes of a double array, this method searches through double values.You can use binary search to solve monotonic functions. For example the following method solves cube root:
BinarySearch.Table<Double, Double> cubeRoot() { return forDoubles() .by(cube -> (low, mid, high) -> Double.compare(cube, mid * mid * mid)); } cubeRoot().insertionPointFor(125.0) => InsertionPoint.at(5.0)This is an O(1) operation.
-
forDoubles
public static BinarySearch.Table<BinarySearch.DoubleSearchTarget, Double> forDoubles(Range<Double> range) Similar toforInts(Range), but returns aBinarySearch.Tableover the givenrangeofdoubleprecision numbers.Callers can search by a
BinarySearch.DoubleSearchTargetobject that will be called at each probe to determine whether the target is already found at the current mid-point, to the left half of the current subrange, or to the right half of the current subrange.For example you can implement square root through binary search:
BinarySearch.Table<Double, Double> sqrt() { return forDoubles(atLeast(0D)) .by(square -> (low, mid, high) -> Double.compare(square, mid * mid)); } sqrt().insertionPointFor(25.0) => InsertionPoint.at(5.0)Infinite endpoints are disallowed.
This is an O(1) operation.
-