Here’s a clean, practical tour of all bisect functions in Python – with code and scenario-based examples.
We’ll cover:
-
bisect_left -
bisect_right(same asbisect) -
insort_left -
insort_right(same asinsort)
All of these work on sorted lists. If the list is not sorted, results are wrong.
1. Basic idea: using bisect_left and bisect_right
from bisect import bisect_left, bisect_right
scores = [10, 20, 20, 20, 30, 40] # sorted list
x = 20
pos_left = bisect_left(scores, x)
pos_right = bisect_right(scores, x)
print("scores:", scores)
print("bisect_left(20) ->", pos_left) # first position where 20 can go (leftmost)
print("bisect_right(20) ->", pos_right) # position after the last 20 (rightmost+1)
Output (conceptually):
-
bisect_left(20)→1 -
bisect_right(20)→4
When to use which?
-
bisect_left(a, x)-
Think: “insert before existing equals”
-
Good for lower bound, “first ≥ x”
-
-
bisect_right(a, x)/bisect(a, x)-
Think: “insert after existing equals”
-
Good for upper bound, “first > x”
-
2. Scenario 1 – Grade boundaries (using bisect_right)
You have percentage marks, and you want to map them to grades based on boundary cutoffs.
from bisect import bisect_right
# boundaries: marks at which grade changes
boundaries = [40, 60, 75, 90] # must be sorted
grades = ["F", "C", "B", "A", "A+"] # one more than boundaries
def get_grade(score):
idx = bisect_right(boundaries, score)
return grades[idx]
for s in [35, 40, 59, 60, 74, 75, 89, 90, 99]:
print(f"{s} -> {get_grade(s)}")
Logic:
-
bisect_right(boundaries, score)returns how many boundaries the score crosses. -
We use that number as the index in
grades.
3. Scenario 2 – Find position to insert (using bisect_left)
You want to place a new value in a sorted list but without actually inserting – just find where it would go.
from bisect import bisect_left
data = [3, 5, 8, 12, 15]
def find_insert_position(sorted_list, value):
pos = bisect_left(sorted_list, value)
return pos
for v in [1, 5, 7, 20]:
pos = find_insert_position(data, v)
print(f"Value {v} should go at index {pos} in {data}")
Use this when you just need the index, e.g., to split a list, or for binary search style lookup.
4. Inserting while keeping the list sorted – insort_left & insort_right
These are like bisect_... + list.insert(...) combined.
from bisect import insort_left, insort_right
nums = [10, 20, 20, 30]
print("Original:", nums)
# Insert with insort_left (before existing equals)
insort_left(nums, 20)
print("After insort_left(20): ", nums)
# Insert with insort_right (after existing equals)
insort_right(nums, 20)
print("After insort_right(20):", nums)
Behavior:
-
insort_left(a, x)
Insertxat the position returned bybisect_left(a, x)(before equals) -
insort_right(a, x)/insort(a, x)
Insertxat the position returned bybisect_right(a, x)(after equals)
5. Scenario 3 – Maintaining a sorted list (online data)
Imagine you get sensor readings one by one, and you want to keep them in sorted order to quickly get median or percentiles.
from bisect import insort
readings = []
def add_reading(value):
# insort == insort_right
insort(readings, value)
def get_median():
n = len(readings)
if n == 0:
return None
mid = n // 2
if n % 2 == 1:
return readings[mid]
else:
return (readings[mid - 1] + readings[mid]) / 2
for r in [10, 5, 8, 15, 3, 12]:
add_reading(r)
print("After adding", r, "=> readings:", readings, "median:", get_median())
Here:
-
New values are inserted in O(n) time but still using binary search internally for the position.
-
Lookup for median is O(1) once sorted.
6. Scenario 4 – Counting elements in a range
Given a sorted list, how many values fall between low and high?
from bisect import bisect_left, bisect_right
data = [2, 4, 4, 4, 7, 9, 10, 10, 15] # sorted
def count_in_range(sorted_list, low, high):
# first index with value >= low
left = bisect_left(sorted_list, low)
# first index with value > high
right = bisect_right(sorted_list, high)
return right - left
print(count_in_range(data, 4, 10)) # how many values between 4 and 10 inclusive?
print(count_in_range(data, 5, 9)) # values between 5 and 9
print(count_in_range(data, 0, 100)) # all values
Key idea:
-
left= position of first value ≥low -
right= position of first value >high -
count =
right - left
7. Scenario 5 – Implementing your own binary search using bisect_left
You can use bisect_left as a helper to implement “does this value exist?” in a sorted list.
from bisect import bisect_left
def contains(sorted_list, value):
i = bisect_left(sorted_list, value)
# i is index where value *should* be inserted to stay sorted
if i != len(sorted_list) and sorted_list[i] == value:
return True
return False
arr = [1, 3, 4, 7, 9, 11]
for v in [3, 5, 11, 12]:
print(f"{v} exists? -> {contains(arr, v)}")
8. Quick summary of all bisect functions
from bisect import (
bisect_left,
bisect_right, # same as bisect
bisect, # alias of bisect_right
insort_left,
insort_right, # same as insort
insort, # alias of insort_right
)
-
Search / index only
-
bisect_left(a, x)→ index of first element ≥ x -
bisect_right(a, x)/bisect(a, x)→ index of first element > x
-
-
Insert while keeping list sorted
-
insort_left(a, x)→ insert at positionbisect_left(a, x) -
insort_right(a, x)/insort(a, x)→ insert at positionbisect_right(a, x)
-
All of them assume a is already sorted.
No comments:
Post a Comment