Introduction
Bisect Left is a function used to perform binary search on a sorted list to determine the leftmost (first) insertion point for a given element. It is particularly useful when working with sorted data and when you need to maintain ordering while inserting new elements or searching for positions.
In Python, the bisect_left() function is provided by the built-in bisect module. It performs O(log n) search time and is widely used in efficient searching, insertion, and algorithmic problem-solving.
This function is foundational for implementing binary search logic, solving range queries, and optimizing algorithms that require searching within a sorted sequence.
What Is Bisect Left?
bisect_left(a, x) returns the index where x should be inserted in the list a to maintain order, placing it before any existing entries of x.
If x is already present in the list, the function returns the index of the first occurrence of x.
In other words, it answers the question:
“Where is the leftmost position I can insert
xinto the listaand keep it sorted?”
Syntax
import bisect
bisect.bisect_left(a, x, lo=0, hi=len(a))
Parameters:
a: The sorted list.x: The element to find the insertion point for.lo: The starting index for search (default:0).hi: The ending index for search (default:len(a)).
Returns:
- An integer representing the leftmost insertion index.
Example: Basic Usage
import bisect
data = [10, 20, 20, 20, 30, 40]
index = bisect.bisect_left(data, 20)
print(index) # Output: 1
Here, 20 appears multiple times. bisect_left returns 1, the index of the first occurrence.
Use Case: Inserting While Maintaining Order
import bisect
def insert_sorted(data, x):
index = bisect.bisect_left(data, x)
data.insert(index, x)
lst = [10, 20, 30, 40]
insert_sorted(lst, 25)
print(lst) # Output: [10, 20, 25, 30, 40]
This is highly efficient compared to sorting the list after every insertion.
Comparison with bisect_right
The bisect module provides two main functions:
| Function | Description |
|---|---|
bisect_left(a, x) | Insert to the left of existing entries of x |
bisect_right(a, x) | Insert to the right of existing entries of x |
Example:
data = [10, 20, 20, 20, 30]
print(bisect.bisect_left(data, 20)) # 1
print(bisect.bisect_right(data, 20)) # 4
This means:
- To find the first occurrence of a value, use
bisect_left - To find the last occurrence (exclusive upper bound), use
bisect_right
Custom Binary Search with bisect_left
Although Python’s built-in function is handy, understanding its logic helps in other languages.
Manual Implementation
def bisect_left_manual(arr, target):
lo, hi = 0, len(arr)
while lo < hi:
mid = (lo + hi) // 2
if arr[mid] < target:
lo = mid + 1
else:
hi = mid
return lo
Edge Cases
1. Empty List
bisect.bisect_left([], 5) # Returns 0
2. All Elements Smaller
bisect.bisect_left([1, 2, 3], 5) # Returns 3
3. All Elements Larger
bisect.bisect_left([10, 20, 30], 5) # Returns 0
Performance
- Time Complexity: O(log n)
- Space Complexity: O(1) (purely read/search, no extra space needed)
Efficient even on large datasets. Outperforms linear scans dramatically for large, sorted arrays.
Real-World Applications
| Domain | Use Case |
|---|---|
| Databases | Index lookups on sorted data |
| Compilers | Syntax symbol lookup tables |
| Web Search | Binary search on sorted documents |
| Gaming | Leaderboard score insertion |
| Machine Learning | Bucketizing values, threshold checks |
| Competitive Coding | Fast range queries, prefix sums |
Use with Custom Objects
Use bisect_left with custom key extraction using bisect_key pattern:
from bisect import bisect_left
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
people = [Person("Alice", 30), Person("Bob", 35), Person("Charlie", 40)]
ages = [p.age for p in people]
index = bisect_left(ages, 35) # returns 1
You can also use functools.cmp_to_key for more advanced control in sorting custom objects before applying bisect.
Advanced: Using bisect_left for Lower Bound
In competitive programming and algorithm problems, bisect_left is often used to find the lower bound:
The smallest index
isuch thatarr[i] >= target.
If you want to know if a value exists in a sorted list:
index = bisect_left(arr, target)
if index < len(arr) and arr[index] == target:
print("Found")
else:
print("Not found")
This avoids linear search and is ideal for large data.
Common Patterns
Find if element exists
def exists(arr, x):
i = bisect.bisect_left(arr, x)
return i < len(arr) and arr[i] == x
Count occurrences of x
def count_occurrences(arr, x):
return bisect.bisect_right(arr, x) - bisect.bisect_left(arr, x)
bisect.insort_left
While bisect_left only returns the index, insort_left inserts the value directly at the leftmost position:
from bisect import insort_left
lst = [10, 20, 30]
insort_left(lst, 20)
print(lst) # [10, 20, 20, 30]
Use it when maintaining a continuously sorted list.
When Not to Use bisect_left
- When the list is not sorted — the behavior is undefined.
- If the list changes very frequently, maintaining a sorted list may become costly.
- For multiple insertions, consider using
SortedListfromsortedcontainersfor performance.
Summary
bisect_left is a fundamental tool for fast, precise searching in sorted lists. It finds the leftmost insertion point for a value in O(log n) time, making it ideal for:
- Maintaining order during insertions
- Performing lower bound searches
- Optimizing algorithms with sorted data
By combining bisect_left with patterns like counting, custom objects, and insortions, developers can build high-performance, elegant solutions to a wide variety of problems.
Related Keywords
- Binary Search
- Bisect Module
- Bisect Right
- Custom Key Binary Search
- Insertion Index
- Insort Left
- Leftmost Index
- Lower Bound
- O Log N Search
- Python Bisect
- Range Query Optimization
- Sorted Array Search
- Sorted Insert
- Upper Bound
- Value Insertion Point









