Sortering och sökning
Kunna implementera binärsökning och en sorteringsalgoritm och analysera dem.
Förkunskaper
Intuition
Binärsökning är hur du slår upp ett ord i en ordbok: öppna i mitten, avgör om ordet ligger före eller efter, upprepa i halvan som återstår.
Varje jämförelse halverar sökområdet. Det ger steg:
| Antal element | Linjärt (värsta fall) | Binärt |
|---|---|---|
| 1 000 | 1 000 | 10 |
| 1 000 000 | 1 000 000 | 20 |
| 1 000 000 000 | 1 000 000 000 | 30 |
En miljard element på trettio jämförelser. Villkoret är att listan är sorterad.
Merge sort bygger på samma halveringsidé, baklänges: dela listan i två, sortera varje halva (rekursivt), och flät ihop de två sorterade halvorna. Att fläta ihop två sorterade listor är enkelt — jämför de främsta och ta den minsta.
Det ger : nivåer av delning, och arbete på varje nivå.
Formellt
De vanliga algoritmerna:
| Algoritm | Snitt | Värsta | Minne | Stabil |
|---|---|---|---|---|
| Bubble sort | O(n²) | O(n²) | O(1) | ja |
| Insertion sort | O(n²) | O(n²) | O(1) | ja |
| Merge sort | O(n log n) | O(n log n) | O(n) | ja |
| Quicksort | O(n log n) | O(n²) | O(log n) | nej |
| Timsort (Pythons) | O(n log n) | O(n log n) | O(n) | ja |
Stabil betyder att element med lika nyckel behåller sin inbördes ordning. Det spelar roll när man sorterar i flera steg: sortera först på förnamn, sedan på efternamn — med en stabil sortering blir personer med samma efternamn sorterade på förnamn.
Pythons sorted är Timsort, en hybrid som letar efter redan sorterade avsnitt («runs») och flätar ihop dem. På delvis sorterad data — vilket verklig data ofta är — blir den nästan linjär.
När lönar det sig att sortera först? Sortering kostar en gång, sedan kostar varje sökning i stället för . Med sökningar:
Med : en enda sökning → sortera inte. Tusen sökningar → sortera.
Men i Python är svaret oftast en annan: behöver du bara «finns detta?» är en set ännu bättre — O(1) per fråga, ingen sortering. Sortering behövs när du vill ha ordning, intervall («alla mellan 10 och 20») eller närmaste värde.
Den klassiska buggen i binärsökning är while vanster < hoger i stället för <=, eller att uppdatera gränserna till mitt i stället för mitt ± 1. Det första missar sista elementet; det andra ger en oändlig loop. Båda är lätta att skriva och svåra att se.
Kod
def binarsok(sorterad, mal):
v, h = 0, len(sorterad) - 1
while v <= h: # <= : annars missas sista elementet
m = (v + h) // 2
if sorterad[m] == mal:
return m
if sorterad[m] < mal:
v = m + 1 # m + 1, inte m — annars oändlig loop
else:
h = m - 1
return -1
def merge_sort(x):
if len(x) <= 1:
return x
m = len(x) // 2
return flata(merge_sort(x[:m]), merge_sort(x[m:]))
def flata(a, b):
ut, i, j = [], 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]: # <= gör sorteringen stabil
ut.append(a[i]); i += 1
else:
ut.append(b[j]); j += 1
return ut + a[i:] + b[j:]
print(binarsok([1, 3, 5, 7, 9, 11], 9)) # 4
print(binarsok([1, 3, 5, 7, 9, 11], 4)) # -1
print(merge_sort([5, 2, 9, 1, 5, 6])) # [1, 2, 5, 5, 6, 9]
# Stabilitet i praktiken: sortera i två steg
elever = [("Svensson", "Bo"), ("Andersson", "Cim"), ("Svensson", "Ada")]
steg1 = sorted(elever, key=lambda e: e[1]) # förnamn
steg2 = sorted(steg1, key=lambda e: e[0]) # efternamn — stabil
print(steg2)
# [('Andersson', 'Cim'), ('Svensson', 'Ada'), ('Svensson', 'Bo')]
# I praktiken: använd standardbiblioteket
import bisect
sorterad = [1, 3, 5, 7, 9]
print(bisect.bisect_left(sorterad, 5)) # 2 — index där 5 börjar
print(bisect.insort(sorterad, 6) or sorterad) # [1, 3, 5, 6, 7, 9]
Behärskning innebär
- Implementerar binärsökning korrekt
- Implementerar och analyserar en sorteringsalgoritm
- Vet när man ska sortera först
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- Python-dokumentationen (PSF-licens) — PSF
- CS Unplugged (CC BY-SA 4.0) — CC BY-SA 4.0
- Wikipedia — Binary search algorithm (CC BY-SA 4.0) — CC BY-SA 4.0