Implementing the Bisect Module for Sorted Lists
The bisect module in Python provides functions for manipulating sorted lists. These functions are highly efficient, often outperforming manual implementations, and are crucial for tasks involving searching and insertion into ordered data structures. Your challenge is to reimplement two core functions of this module: bisect_left and bisect_right.
Problem Description
You are tasked with creating two Python functions, my_bisect_left and my_bisect_right, that behave identically to bisect.bisect_left and bisect.bisect_right respectively. These functions will operate on sorted lists and efficiently determine insertion points for new elements.
my_bisect_left(a, x, lo=0, hi=None):
This function should return an insertion point i such that all elements in a[:i] are less than x, and all elements in a[i:] are greater than or equal to x. In simpler terms, it finds the leftmost position where x can be inserted without violating the sorted order.
my_bisect_right(a, x, lo=0, hi=None):
This function should return an insertion point i such that all elements in a[:i] are less than or equal to x, and all elements in a[i:] are greater than x. This finds the rightmost position where x can be inserted without violating the sorted order.
Key Requirements:
- Both functions must accept a sorted list
a, a valuexto search for/insert, and optionallo(low) andhi(high) indices to define the search range withina. - If
hiis not provided, it should default tolen(a). - If
lois not provided, it should default to0. - The functions must maintain the sorted order of the list after insertion. While you are not implementing the insertion itself, the returned index is critical for this.
- The core logic should utilize a binary search algorithm for efficiency.
Expected Behavior:
- For
my_bisect_left, ifxis already present ina, the returned index will be the index of the first occurrence ofx. - For
my_bisect_right, ifxis already present ina, the returned index will be the index immediately after the last occurrence ofx. - If
xis smaller than all elements ina, the indexlo(or0) should be returned. - If
xis larger than all elements ina, the indexhi(orlen(a)) should be returned.
Edge Cases to Consider:
- Empty list
a. xbeing smaller than all elements.xbeing larger than all elements.xbeing equal to existing elements (duplicates).loandhidefining a sub-range that includes duplicates.
Examples
Example 1:
Input:
a = [1, 2, 4, 4, 5]
x = 4
lo = 0
hi = 5
Output for my_bisect_left: 2
Output for my_bisect_right: 4
Explanation:
my_bisect_leftreturns2becausea[2]is the first4. Elements before index2([1, 2]) are less than4. Elements from index2onwards ([4, 4, 5]) are greater than or equal to4.my_bisect_rightreturns4because it's the position after the last4. Elements before index4([1, 2, 4, 4]) are less than or equal to4. Elements from index4onwards ([5]) are greater than4.
Example 2:
Input:
a = [10, 20, 30, 40, 50]
x = 25
lo = 0
hi = 5
Output for my_bisect_left: 2
Output for my_bisect_right: 2
Explanation:
my_bisect_leftreturns2. Elements before index2([10, 20]) are less than25. Elements from index2onwards ([30, 40, 50]) are greater than or equal to25.my_bisect_rightreturns2. Elements before index2([10, 20]) are less than or equal to25. Elements from index2onwards ([30, 40, 50]) are greater than25.
Example 3: (Edge Case - Sub-range with duplicates)
Input:
a = [1, 2, 2, 2, 3, 4, 5]
x = 2
lo = 1
hi = 5 # Sub-range: [2, 2, 2, 3]
Output for my_bisect_left: 1
Output for my_bisect_right: 4
Explanation:
- The search is confined to indices
1through4(exclusive of5). my_bisect_leftwithina[1:5]([2, 2, 2, 3]) finds the leftmost2at index1relative to the original list.my_bisect_rightwithina[1:5]finds the insertion point after the last2, which is index4relative to the original list.
Constraints
- The input list
awill always be sorted in non-decreasing order. - The list
acan contain duplicate elements. - The elements in
aand the valuexwill be comparable (e.g., all integers, all strings). - The length of
acan be up to 10^5. - The solution should have a time complexity of O(log n), where n is the length of the list
a(or the sub-range defined byloandhi).
Notes
- You should implement the logic yourself using a binary search approach. Do not simply call
bisect.bisect_leftorbisect.bisect_rightwithin your functions. - Consider how to handle the
loandhiparameters to restrict the search space. - The
whileloop condition in a binary search is crucial for correctly handling the boundaries and ensuring convergence. - Think about the loop termination condition and how it relates to the returned index.