Class BinarySearch.Table<K,C extends Comparable<C>>
- Type Parameters:
K
- the search key, usually a target value, but can also be a target locator object likeBinarySearch.IntSearchTarget
.C
- the binary search result, usually the index in the source array or list, but can also be the optimal solution in non-array based bisection algorithms such as the minimum value of a parabola function.
- Enclosing class:
BinarySearch
-
Method Summary
Modifier and TypeMethodDescriptionfinal <Q> BinarySearch.Table
<Q, C> Returns a newBinarySearch.Table
over the same source but transforms the search target using the givenkeyFunction
first.Searches fortarget
and returns the result if found; or else returns empty.abstract InsertionPoint
<C> insertionPointAfter
(K target) Finds the insertion point immediately after the last element that's less than or equal to the target.abstract InsertionPoint
<C> insertionPointBefore
(K target) Finds the insertion point immediately before the first element that's greater than or equal to the target.abstract InsertionPoint
<C> insertionPointFor
(K target) Finds theInsertionPoint
iftarget
were to be added in order.com.google.common.collect.Range
<C> Finds the range of elements that matchtarget
.
-
Method Details
-
find
Searches fortarget
and returns the result if found; or else returns empty.This is an O(logn) operation.
-
rangeOf
Finds the range of elements that matchtarget
.If the target is found at index
i
, the single-element range[i..i]
is returned.If there are ties from index
i
toj
, the closed range[i..j]
is returned.If the target isn't found, an
empty
range is returned whose endpoint is the "insertion point" (where the target would have been inserted without breaking order). The direction of the open endpoint determines whether to insert before or after the point.Invariants for insertion points:
- An open lower endpoint
(i..
meanstarget > i
so the insertion point should be afteri
in order not to break order. - An open upper endpoint
..j)
meanstarget < j
so the insertion point should be beforej
in order not to break order. - A closed lower or upper endpoint means
target >= i
ortarget <= j
respectively, so the insertion point can be either before or after without breaking order.
Therefore:
- For all insertion points except the last one after
MAX_VALUE
, the returned range isclosed-open
[i..i)
, indicating that the insertion point is immediately before endpointi
. - While if the insertion point is after
MAX_VALUE
, the returned range isopen-closed
(MAX_VALUE..MAX_VALUE]
, indicating that the insertion point is immediately after endpointMAX_VALUE
.
If your code needs the insertion point when not found, but doesn't need to find the range of elements otherwise, use
insertionPointFor(K)
instead, which is more intuitive and also faster.Realistically though, unless
target > MAX_VALUE
is meaningful to your use case, it's okay to simply insert the target beforerangeOf().lowerEndpoint()
. This invariant holds regardless whether the target is found or not.This is an O(logn) operation.
- An open lower endpoint
-
insertionPointFor
Finds theInsertionPoint
iftarget
were to be added in order.Specifically, if
target
is found, the insertion point is at the its index; while if not found, the insertion point is between the two adjacent indexes where it could be inserted.Imagine in a Google Doc page, if you have two columns of texts to be rendered into a two-column table, and you want to split the two columns as evenly as possible such that it takes the fewest number of lines overall. you can implement it with binary search:
InsertionPoint optimal = BinarySearch.forInts(Range.closedOpen(1, tableWidth)) .insertionPointFor( (low, w, high) -> Integer.compare( renderWithColumnWidth(text1, w), renderWithColumnWidth(text2, tableWidth - w))); return optimal.exact() .orElseGet( () -> { int lines1 = max( renderWithColumnWidth(text1, optimal.floor()), renderWithColumnWidth(text2, tableWidth - optimal.floor())); int lines2 = max( renderWithColumnWidth(text1, optimal.ceiling()), renderWithColumnWidth(text2, tableWidth - optimal.ceiling())); return lines1 < lines2 ? floor : ceiling; });
This is an O(logn) operation.
-
insertionPointBefore
Finds the insertion point immediately before the first element that's greater than or equal to the target.If
target
is absent,insertionPointBefore(K)
andinsertionPointAfter(K)
will be the same point, after the last element less than the target and the first element greater than it.insertionPointBefore(target).exact()
will always return empty.This is an O(logn) operation.
-
insertionPointAfter
Finds the insertion point immediately after the last element that's less than or equal to the target.If
target
is absent,insertionPointBefore(K)
andinsertionPointAfter(K)
will be the same point, after the last element less than the target and the first element greater than it.insertionPointAfter(target).exact()
will always return empty.This is an O(logn) operation.
-
by
Returns a newBinarySearch.Table
over the same source but transforms the search target using the givenkeyFunction
first.Useful for creating a facade in front of a lower-level backing data source.
For example, if you have epoch millisecond timestamps stored in a sorted
long[]
array, you can wrap it as aBinarySearch.Table<Instant, Integer>
for your code to be able to search byInstant
repeatedly:class Timeline { private final long[] timestamps; BinarySearch.Table<Instant, Integer> instants() { return BinarySearch.inSortedArray(timestamps).by(Instant::toEpochMilli); } }
This is an O(1) operation.
- Type Parameters:
Q
- the logical search key type of the returnedBinarySearch.Table
.
-