A new binary-search attempt

Continuing the discussion from An always-sorted set:

I just updated the gist to Revision 2, which updates the "BinarySearch.swift" file. I did a rough search on this forum and found:

The sequence/collection types were different back then, but I hope my interpretation works nowadays.

Instead of returning false or nil if a binary search fails, I return where that element value should go if you want to insert it into the collection (if its type included RangeReplaceableCollection). I now realize that this is like the C++ std::upper_bound function. From the old posts, I created a partitionIndex(by:) function that generalizes the binary search. It uses the same kind of predicate as MutableCollection.partition(by:). It is used by firstIndexOfSorted(exceeding: by:), and both are moved from RandomAccessCollection to Collection, where the computation costs becomes O(n) except for random-access, where it's O(log n), where n is the collection length.

When the collection elements are Comparable, a firstIndexOfSorted(exceeding:) method is added for the common case that < is the predicate.

Reading about the C++ std::equal_range function, I made an equivalent indicesOfSorted(for: by:) method that returns a Range<Index>. An analogue of the C++ std::binary_search can be done by checking if the range returned is not empty, while an analogue of std::lower_bound and alternate for std::upper_bound can be calculated by checking a bound of the returned range. There is a Comparable optimization in indicesOfSorted(for:).

Right before writing those, I realized we could make a method that searches for a zone of element values. It's still the dual partition check, but the partition bounds now differ. It's the indicesOfSorted(over:) method.

1 Like