Ordered Set
When you need the predecessor or successor of a value in O(log n), a sorted structure beats a dict or heap. SortedList maintains order on every insert and delete — the Python equivalent of C++'s std::set.
Hash map: O(1) exact lookup but O(n) for predecessor/successor. Sorted structure: O(log n) for everything — insert, delete, predecessor, successor, range min/max.
The pattern activates when you need to answer: "what is the nearest value smaller/larger than x in my current set?" This is the predecessor/successor query — impossible in O(log n) with a hash map, trivial with a sorted structure and bisect.
bisect + SortedList Cheatsheet
Two tools: bisect module (built-in) and sortedcontainers.SortedList (pip).
bisect_left(a,x)index of first elem ≥ xbisect_right(a,x)index of first elem > xinsort(a,x)insert x keeping sorted (O(n) shift)a[bisect_left(a,x)-1]predecessor (largest < x)a[bisect_left(a,x)]successor (smallest ≥ x)sl.add(x)O(log n) insertsl.remove(x)O(log n), raises if absentsl.discard(x)O(log n), safe (no error)sl[0] / sl[-1]min / max O(log n)sl.bisect_left(x)same semantics as bisect moduleThe 3-Step Framework
Sort → bisect to position → check predecessor/successor.
bisect on a plain list works for read-heavy problems (many queries, few inserts). SortedList is better when elements are added/removed dynamically (calendar booking, sliding window). For static data, sort once and use bisect throughout.
# Static (sort once, many queries): nums.sort() idx = bisect_left(nums, target) # Dynamic (inserts + queries): from sortedcontainers import SortedList sl = SortedList() sl.add(x) # O(log n)
bisect_left(sl, x) gives the index where x would be inserted. The predecessor is at index-1 (if exists), the successor is at that index (if exists). Always bounds-check before accessing.
idx = sl.bisect_left(x)
# Predecessor: largest value < x
if idx > 0:
pred = sl[idx - 1]
# Successor: smallest value >= x
if idx < len(sl):
succ = sl[idx]Sliding window + SortedList: after adding right element, while sl[-1]-sl[0] > limit, remove the left element. The SortedList always gives O(log n) max and min via sl[-1] and sl[0]. This beats two monotonic deques when removals aren't from the ends.
sl = SortedList()
left = 0
for right, v in enumerate(nums):
sl.add(v)
while sl[-1] - sl[0] > limit:
sl.remove(nums[left])
left += 1
result = max(result, right-left+1)Pattern Recognition
'predecessor', 'nearest smaller/larger', 'sorted window' → ordered set.