diff --git a/python/bisect.py b/python/bisect.py index a0df9bc7..eee23839 100644 --- a/python/bisect.py +++ b/python/bisect.py @@ -1,72 +1,123 @@ -"""Bisection algorithms.""" - -def insort_right(a, x, lo=0, hi=None): - """Insert item x in list a, and keep it sorted assuming a is sorted. - - If x is already in a, insert it to the right of the rightmost x. - - Optional args lo (default 0) and hi (default len(a)) bound the - slice of a to be searched. - """ - - lo = bisect_right(a, x, lo, hi) - a.insert(lo, x) - +""" +Optimized bisection algorithms for sorted list insertions. +These functions provide efficient binary search implementations for finding insertion points +in sorted sequences and inserting elements while maintaining sorted order. +""" def bisect_right(a, x, lo=0, hi=None): """Return the index where to insert item x in list a, assuming a is sorted. - + The return value i is such that all e in a[:i] have e <= x, and all e in - a[i:] have e > x. So if x already appears in the list, a.insert(x) will - insert just after the rightmost x already there. - - Optional args lo (default 0) and hi (default len(a)) bound the - slice of a to be searched. + a[i:] have e > x. So if x already appears in the list, the insertion + point will be after (to the right of) any existing entries. + + Args: + a: A sorted list-like object supporting comparison and __len__ + x: The item to be inserted + lo: Lower bound of the slice to be searched (inclusive) + hi: Upper bound of the slice to be searched (exclusive) + + Returns: + The index where x should be inserted to maintain sorted order + + Raises: + ValueError: If lo is negative """ - if lo < 0: raise ValueError('lo must be non-negative') + if hi is None: hi = len(a) + + # Fast path for common case: appending to the end + if hi > 0 and hi == len(a) and x > a[-1]: + return hi + + # Normal binary search while lo < hi: - mid = (lo+hi)//2 - if x < a[mid]: hi = mid - else: lo = mid+1 + mid = (lo + hi) // 2 + if x < a[mid]: + hi = mid + else: + lo = mid + 1 + return lo -def insort_left(a, x, lo=0, hi=None): - """Insert item x in list a, and keep it sorted assuming a is sorted. - - If x is already in a, insert it to the left of the leftmost x. - - Optional args lo (default 0) and hi (default len(a)) bound the - slice of a to be searched. - """ - - lo = bisect_left(a, x, lo, hi) - a.insert(lo, x) - def bisect_left(a, x, lo=0, hi=None): """Return the index where to insert item x in list a, assuming a is sorted. - + The return value i is such that all e in a[:i] have e < x, and all e in - a[i:] have e >= x. So if x already appears in the list, a.insert(x) will - insert just before the leftmost x already there. - - Optional args lo (default 0) and hi (default len(a)) bound the - slice of a to be searched. + a[i:] have e >= x. So if x already appears in the list, the insertion + point will be before (to the left of) any existing entries. + + Args: + a: A sorted list-like object supporting comparison and __len__ + x: The item to be inserted + lo: Lower bound of the slice to be searched (inclusive) + hi: Upper bound of the slice to be searched (exclusive) + + Returns: + The index where x should be inserted to maintain sorted order + + Raises: + ValueError: If lo is negative """ - if lo < 0: raise ValueError('lo must be non-negative') + if hi is None: hi = len(a) + + # Fast path for common case: appending to the end + if hi > 0 and hi == len(a) and x > a[-1]: + return hi + + # Fast path for common case: inserting at the beginning + if lo == 0 and hi > 0 and x < a[0]: + return 0 + + # Normal binary search while lo < hi: - mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 - else: hi = mid + mid = (lo + hi) // 2 + if a[mid] < x: + lo = mid + 1 + else: + hi = mid + return lo -# Create aliases + +def insort_right(a, x, lo=0, hi=None): + """Insert item x in list a, and keep it sorted assuming a is sorted. + + If x is already in a, insert it to the right of the rightmost x. + + Args: + a: A sorted list-like object supporting comparison, __len__, and insert + x: The item to be inserted + lo: Lower bound of the slice to be searched (inclusive) + hi: Upper bound of the slice to be searched (exclusive) + """ + # Use bisect_right to find the insertion point + insertion_point = bisect_right(a, x, lo, hi) + a.insert(insertion_point, x) + + +def insort_left(a, x, lo=0, hi=None): + """Insert item x in list a, and keep it sorted assuming a is sorted. + + If x is already in a, insert it to the left of the leftmost x. + + Args: + a: A sorted list-like object supporting comparison, __len__, and insert + x: The item to be inserted + lo: Lower bound of the slice to be searched (inclusive) + hi: Upper bound of the slice to be searched (exclusive) + """ + # Use bisect_left to find the insertion point + insertion_point = bisect_left(a, x, lo, hi) + a.insert(insertion_point, x) + +# Create aliases for backward compatibility bisect = bisect_right insort = insort_right