Bisect-moduulin vaativuus¶
Moduuli bisect tarjoaa binäärihakuoperaatioita järjestetyille listoille.
Operaatiot¶
| Operaatio | Aika | Tila | Huomiot |
|---|---|---|---|
bisect_left(a, x) |
O(log n) | O(1) | Etsii vasemmanpuoleisimman sijainnin |
bisect_right(a, x) |
O(log n) | O(1) | Etsii oikeanpuoleisimman sijainnin |
bisect(a, x) |
O(log n) | O(1) | Vaihtoehtoinen nimi funktiolle bisect_right |
insort_left(a, x) |
O(n) | O(1) | O(log n) haku + O(n) lisäys (siirtää alkioita paikallaan) |
insort_right(a, x) |
O(n) | O(1) | O(log n) haku + O(n) lisäys (siirtää alkioita paikallaan) |
insort(a, x) |
O(n) | O(1) | Vaihtoehtoinen nimi funktiolle insort_right |
Tilavaativuus¶
- Binäärihakuoperaatiot: O(1) lisätilaa
- Lisäysoperaatiot: O(1) lisätilaa (siirtää alkioita olemassa olevan listan sisällä)
Toteutuksen yksityiskohdat¶
Binäärihaun takuu¶
import bisect
# Must be sorted!
sorted_list = [1, 3, 3, 3, 5, 7, 9]
# bisect_left: leftmost insertion point
pos = bisect.bisect_left(sorted_list, 3) # O(log n), pos = 1
# Insert here to keep list sorted (before all 3's)
# bisect_right: rightmost insertion point
pos = bisect.bisect_right(sorted_list, 3) # O(log n), pos = 4
# Insert here to keep list sorted (after all 3's)
# Both halve the search range each step, so a run of equal values costs no
# more than a unique one
Alkioiden etsiminen¶
import bisect
sorted_list = [1, 3, 5, 7, 9]
# Check if element exists
def exists(sorted_list, x):
pos = bisect.bisect_left(sorted_list, x)
return pos < len(sorted_list) and sorted_list[pos] == x
exists(sorted_list, 5) # True - O(log n)
exists(sorted_list, 4) # False - O(log n)
Yleiset käyttötapaukset¶
Lisäys järjestystä säilyttäen¶
import bisect
sorted_list = [1, 3, 5, 7]
# Insert while maintaining order - O(n) overall
# (O(log n) search + O(n) shift)
bisect.insort(sorted_list, 4) # [1, 3, 4, 5, 7]
# Better for many insertions: use list, then sort
# Multiple inserts: O(n log n) with sort
# vs O(n²) with repeated insort
Välien etsiminen¶
import bisect
# Find all equal elements
sorted_list = [1, 3, 3, 3, 5, 7, 9]
target = 3
left = bisect.bisect_left(sorted_list, target)
right = bisect.bisect_right(sorted_list, target)
equals = sorted_list[left:right] # All 3's - O(log n) search
Välin lisäyskohdan etsiminen¶
import bisect
# Find where range [a, b] fits in sorted list
sorted_list = [1, 5, 10, 15, 20]
target_range = (7, 12)
# Position to insert start of range
start_pos = bisect.bisect_right(sorted_list, target_range[0]) # O(log n)
# Position to insert end of range
end_pos = bisect.bisect_left(sorted_list, target_range[1]) # O(log n)
# Two independent searches: O(log n) total, not O(n)
print(f"Insert range {target_range} at positions {start_pos}-{end_pos}")
Suorituskyvyn vertailu¶
Haku järjestetystä datasta¶
import bisect
data = sorted(range(1000000))
# Bad: Linear search - O(n)
found = 500000 in data # Scans linearly
# Good: Binary search - O(log n)
pos = bisect.bisect_left(data, 500000) # Much faster!
found = pos < len(data) and data[pos] == 500000
Järjestettyjen listojen ylläpito¶
import bisect
# Many insertions scenario
sorted_list = [1, 3, 5, 7, 9]
# Bad: Multiple insort - O(n²)
for item in [2, 4, 6, 8]:
bisect.insort(sorted_list, item) # O(n) each
# Better: Collect, sort once - O(n log n)
sorted_list.extend([2, 4, 6, 8])
sorted_list.sort() # Single O(n log n) operation
Yksityiskohtaiset esimerkit¶
Arvosanavälit¶
import bisect
# Map scores to grades
grade_breaks = [60, 70, 80, 90]
grades = ['F', 'D', 'C', 'B', 'A']
def get_grade(score):
i = bisect.bisect(grade_breaks, score)
return grades[i]
print(get_grade(85)) # 'B' - O(log n)
print(get_grade(95)) # 'A' - O(log n)
Aikaleimahaku¶
import bisect
from datetime import datetime, timedelta
# Find events in a time range
events = [
(datetime(2024, 1, 1, 10), 'event1'),
(datetime(2024, 1, 1, 12), 'event2'),
(datetime(2024, 1, 1, 15), 'event3'),
(datetime(2024, 1, 1, 18), 'event4'),
]
timestamps = [e[0] for e in events]
# Find events after specific time
target = datetime(2024, 1, 1, 14)
idx = bisect.bisect_right(timestamps, target)
later_events = events[idx:] # O(log n) search
print(later_events) # Events at 3pm and 6pm
Edistynyt: omat avainfunktiot¶
import bisect
from bisect import bisect_right
# Custom objects - compare by second element
data = [('a', 1), ('b', 3), ('c', 5)]
keys = [x[1] for x in data] # O(n) - building the key list dominates
# Find position for ('d', 4)
pos = bisect_right(keys, 4) # O(log n)
data.insert(pos, ('d', 4)) # O(n) - shifts the tail
# Rebuilding keys per search makes the whole thing O(n); keep it alongside
# data instead
Python 3.10 alkaen jokainen moduulin funktio ottaa key-argumentin, joka
poistaa rinnakkaisen listan tarpeen:
import bisect
data = [('a', 1), ('b', 3), ('c', 5)]
# key runs once per probe, so no parallel list is needed
pos = bisect.bisect_right(data, 4, key=lambda item: item[1]) # O(log n) key calls
data.insert(pos, ('d', 4)) # O(n) - shifts the tail
Valinta riippuu siitä, kuinka moni haku jakaa samat avaimet: rinnakkainen lista
maksaa O(n) kerran eikä yhtään kutsua hakua kohti, kun taas key ei maksa
mitään etukäteen ja yhden kutsun jokaista koetinta kohti.
Tärkeitä huomioita¶
Vaatimus järjestetystä datasta
Syötelistan TÄYTYY olla järjestetty, jotta binäärihaku toimii oikein.
import bisect
# Wrong: Data not sorted
unsorted = [3, 1, 4, 1, 5]
pos = bisect.bisect(unsorted, 2) # Incorrect result!
Tasoitettu tehokkuus
Kun lisäyksiä on paljon:
- Useita
insort()-kutsuja: yhteensä O(n²) - Kerää ensin ja kutsu
sort()kerran: yhteensä O(n log n)
Valitse käyttötapasi mukaan.
Versiohuomiot¶
- Python 3.10+:
key-parametri lisättiin jokaiseen moduulin funktioon