Tilbake
3.4
Søke- og sorteringsalgoritmer

3.4 Søke- og sorteringsalgoritmer

Lær lineært søk, binærsøk, boblesortering, innsettingssortering og utvalgssortering.

60 min
7 oppgaver
Lineært søkBinærsøkBoblesorteringInnsettingssortering
Du leser den tradisjonelle versjonen
Din fremgang i kapitlet
0 / 7 oppgaver

Søke- og sorteringsalgoritmer

Tenk deg at du har en telefonkatalog med 10 000 navn. Hvis du skal finne nummeret til «Olsen», kan du starte fra begynnelsen og sjekke hvert navn. Men det er jo ikke slik du faktisk bruker en telefonkatalog. Du slår opp omtrent midt i boken, ser at du er på «M», og vet at «O» er litt lenger bak. Deretter halverer du igjen og igjen til du finner riktig side. Denne intuitive metoden er faktisk en av de viktigste algoritmene i informatikk: binærsøk.

Søking og sortering er to av de mest grunnleggende operasjonene i informatikk. Praktisk talt alle programmer som arbeider med data, må søke i data og sortere data. E-postprogrammet ditt sorterer meldinger etter dato. Nettbutikken sorterer produkter etter pris. Søkemotorer søker gjennom milliarder av nettsider for å finne det du leter etter.

I dette kapittelet skal du lære å implementere de mest kjente søke- og sorteringsalgoritmene i Python, forstå hvordan de fungerer, og sammenligne dem med hensyn til effektivitet.

Lineært søk
Lineært søk (også kalt sekvensielt søk) er den enkleste søkealgoritmen. Den starter fra begynnelsen av listen og sjekker hvert element i rekkefølge til den enten finner det søkte elementet eller har gått gjennom hele listen uten å finne det. Lineært søk fungerer på alle lister, uavhengig av om de er sorterte eller ikke. I verste fall (elementet er sist eller ikke finnes) må den sjekke alle n elementer, noe som gjør den treg for svært store datasett.

Lineært søk

Lineært søk er som å lete etter en bestemt bok i en usortert bokhylle. Du begynner med den første boken, sjekker om det er riktig bok, og hvis ikke, går du videre til neste. Du fortsetter til du finner boken eller har sjekket alle.

Algoritme:
1. Start fra det første elementet i listen
2. Sammenlign elementet med det du søker etter
3. Hvis det er likt, returner posisjonen
4. Hvis ikke, gå til neste element
5. Hvis du har sjekket alle elementer uten å finne, returner «ikke funnet»

def lineaert_sok(liste, sokeord):
    """Søker etter sokeord i listen. Returnerer indeksen eller -1."""
    for i in range(len(liste)):
        if liste[i] == sokeord:
            return i  # Funnet på posisjon i
    return -1  # Ikke funnet

# Eksempel
tall = [4, 7, 2, 9, 1, 5, 8, 3, 6]

resultat = lineaert_sok(tall, 5)
if resultat != -1:
    print(f"Fant 5 på posisjon {resultat}")
else:
    print("Fant ikke 5")

resultat = lineaert_sok(tall, 10)
if resultat != -1:
    print(f"Fant 10 på posisjon {resultat}")
else:
    print("Fant ikke 10")

Kjøring:

Fant 5 på posisjon 5
Fant ikke 10

Analyse: For en liste med n elementer må lineært søk i gjennomsnitt sjekke n/2 elementer, og i verste fall alle n. Vi sier at lineært søk har tidskompleksitet O(n), der n er antall elementer. For en liste med 1 000 000 elementer kan det ta opptil 1 000 000 sammenligninger.

Binærsøk
Binærsøk er en svært effektiv søkealgoritme som krever at listen er sortert på forhånd. Algoritmen sammenligner det søkte elementet med midtelementet i listen. Hvis det søkte er mindre, fortsetter søket i venstre halvdel. Hvis det er større, fortsetter søket i høyre halvdel. Slik halveres søkeområdet for hvert steg. For en sortert liste med n elementer trenger binærsøk maksimalt log₂(n) sammenligninger, noe som er dramatisk raskere enn lineært søk for store datasett.

Binærsøk

Binærsøk er som å slå opp i en telefonkatalog. Fordi navnene er sortert alfabetisk, kan du halvere søkeområdet i hvert steg.

Algoritme:
1. Sett lav til 0 og hoy til siste indeks
2. Finn midt = (lav + hoy) // 2
3. Hvis elementet på midt er det du søker, returner midt
4. Hvis elementet på midt er for stort, sett hoy = midt - 1
5. Hvis elementet på midt er for lite, sett lav = midt + 1
6. Gjenta fra steg 2 så lenge lav <= hoy
7. Hvis lav > hoy, er elementet ikke i listen

def binaersok(sortert_liste, sokeord):
    """Binærsøk i en sortert liste. Returnerer indeksen eller -1."""
    lav = 0
    hoy = len(sortert_liste) - 1

    while lav <= hoy:
        midt = (lav + hoy) // 2

        if sortert_liste[midt] == sokeord:
            return midt  # Funnet!
        elif sortert_liste[midt] < sokeord:
            lav = midt + 1  # Søk i høyre halvdel
        else:
            hoy = midt - 1  # Søk i venstre halvdel

    return -1  # Ikke funnet

# Eksempel
sortert = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

print(binaersok(sortert, 7))   # 3 (posisjon 3)
print(binaersok(sortert, 12))  # -1 (ikke funnet)

Trinnvis gjennomgang for å finne 7 i [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]:

SteglavhoymidtVerdi på midtHandling
109499 > 7, hoy = 3
203133 < 7, lav = 2
323255 < 7, lav = 3
43337Funnet!

Bare 4 sammenligninger for å finne tallet i en liste med 10 elementer! For en liste med 1 000 000 elementer trenger binærsøk maksimalt 20 sammenligninger (log₂(1 000 000) ≈ 20), sammenlignet med 1 000 000 for lineært søk.

Boblesortering (Bubble Sort)

Boblesortering er den enkleste sorteringsalgoritmen å forstå. Ideen er å gjentatte ganger gå gjennom listen og sammenligne naboelementer. Hvis to naboelementer er i feil rekkefølge, bytter vi dem. Vi gjentar dette til listen er sortert.

Navnet «boblesortering» kommer av at de største elementene gradvis «bobler opp» til riktig posisjon, akkurat som luftbobler stiger til overflaten i et glass vann.

def boblesortering(liste):
    """Sorterer en liste med boblesortering."""
    n = len(liste)

    for i in range(n - 1):
        byttet = False

        for j in range(n - 1 - i):
            if liste[j] > liste[j + 1]:
                # Bytt naboelementer
                liste[j], liste[j + 1] = liste[j + 1], liste[j]
                byttet = True

        # Optimalisering: hvis ingen bytter ble gjort, er listen sortert
        if not byttet:
            break

    return liste

# Eksempel
tall = [64, 34, 25, 12, 22, 11, 90]
print(f"Usortert: {tall}")
boblesortering(tall)
print(f"Sortert:  {tall}")

Kjøring:

Usortert: [64, 34, 25, 12, 22, 11, 90]
Sortert:  [11, 12, 22, 25, 34, 64, 90]

Trinnvis eksempel med [5, 3, 1, 4, 2]:

Gjennomgang 1: [3, 1, 4, 2, 5] (5 bobler opp til slutten)
Gjennomgang 2: [1, 3, 2, 4, 5] (4 på plass)
Gjennomgang 3: [1, 2, 3, 4, 5] (3 på plass)
Gjennomgang 4: [1, 2, 3, 4, 5] (ingen bytter, ferdig!)

Boblesortering er enkel og intuitiv, men den er treg for store datasett fordi den i verste fall trenger omtrent n² sammenligninger.

Innsettingssortering (Insertion Sort)

Innsettingssortering fungerer som du sorterer spillkort på hånden. Du tar ett kort om gangen fra bunken og setter det inn på riktig plass blant de kortene du allerede holder sortert.

Algoritme:
1. Start med det andre elementet (det første er «sortert» alene)
2. Ta elementet og sammenlign det med elementene til venstre
3. Flytt elementene som er større ett steg til høyre
4. Sett inn elementet på riktig plass
5. Gjenta for alle gjenværende elementer

def innsettingssortering(liste):
    """Sorterer en liste med innsettingssortering."""
    for i in range(1, len(liste)):
        nokkel = liste[i]  # Elementet vi skal sette inn
        j = i - 1

        # Flytt elementer som er større enn nøkkelen
        while j >= 0 and liste[j] > nokkel:
            liste[j + 1] = liste[j]
            j -= 1

        # Sett inn nøkkelen på riktig plass
        liste[j + 1] = nokkel

    return liste

# Eksempel
tall = [5, 3, 1, 4, 2]
print(f"Usortert: {tall}")
innsettingssortering(tall)
print(f"Sortert:  {tall}")

Trinnvis for [5, 3, 1, 4, 2]:

StegNøkkelHandlingResultat
133 < 5, flytt 5, sett inn 3[3, 5, 1, 4, 2]
211 < 5, flytt 5; 1 < 3, flytt 3; sett inn 1[1, 3, 5, 4, 2]
344 < 5, flytt 5; 4 > 3, sett inn 4[1, 3, 4, 5, 2]
422 < 5, flytt; 2 < 4, flytt; 2 < 3, flytt; 2 > 1, sett inn[1, 2, 3, 4, 5]

Innsettingssortering er spesielt effektiv for lister som allerede er nesten sorterte, fordi den da gjør svært få flytteoperasjoner.

Utvalgssortering (Selection Sort)

Utvalgssortering er en annen enkel sorteringsalgoritme. Ideen er: finn det minste elementet og plasser det først. Finn deretter det nest minste og plasser det på posisjon 2, og så videre.

def utvalgssortering(liste):
    """Sorterer en liste med utvalgssortering."""
    n = len(liste)

    for i in range(n - 1):
        # Finn indeksen til det minste elementet i resten av listen
        min_indeks = i
        for j in range(i + 1, n):
            if liste[j] < liste[min_indeks]:
                min_indeks = j

        # Bytt det minste elementet med elementet på posisjon i
        liste[i], liste[min_indeks] = liste[min_indeks], liste[i]

    return liste

# Eksempel
tall = [29, 10, 14, 37, 13]
print(f"Usortert: {tall}")
utvalgssortering(tall)
print(f"Sortert:  {tall}")

Trinnvis for [29, 10, 14, 37, 13]:

StegFinner minstBytteResultat
110 (pos 1)Bytt 29 og 10[10, 29, 14, 37, 13]
213 (pos 4)Bytt 29 og 13[10, 13, 14, 37, 29]
314 (pos 2)Allerede riktig[10, 13, 14, 37, 29]
429 (pos 4)Bytt 37 og 29[10, 13, 14, 29, 37]

Utvalgssortering gjør alltid nøyaktig n(n-1)/2 sammenligninger, uavhengig av om listen er sortert eller ikke. Den er enkel å forstå, men ikke spesielt effektiv.

Sammenligning av søke- og sorteringsalgoritmer

Søkealgoritmer:

AlgoritmeKravVerste fallKommentar
Lineært søkIngenn sammenligningerEnkel, fungerer alltid
BinærsøkSortert listelog₂(n) sammenligningerSvært rask for store datasett

For å illustrere forskjellen: i en liste med 1 000 000 elementer gjør lineært søk opptil 1 000 000 sammenligninger, mens binærsøk gjør maksimalt 20!
Sorteringsalgoritmer:
AlgoritmeVerste fallBeste fallStabil?Kommentar
Boblesorteringn (optimalisert)JaEnklest å forstå
InnsettingssorteringnJaBest for nesten sorterte data
UtvalgssorteringNeiFærrest bytteoperasjoner

Alle tre sorteringsalgoritmene har tidskompleksitet O(n²) i verste fall, noe som betyr at de blir svært trege for store datamengder. I praksis bruker Python den innebygde funksjonen sorted() eller metoden .sort(), som bruker Timsort-algoritmen med tidskompleksitet O(n log n), noe som er vesentlig raskere.

# Pythons innebygde sortering (Timsort) – bruk denne i praksis!
tall = [64, 34, 25, 12, 22, 11, 90]
sortert = sorted(tall)        # Lager ny sortert liste
tall.sort()                    # Sorterer listen direkte

Likevel er det verdifullt å forstå de enklere algoritmene, fordi de illustrerer grunnleggende konsepter som sammenligning, bytting og iterasjon, og fordi de er et godt utgangspunkt for å forstå algoritmekompleksitet.

✏️Praktisk bruk: sortering og søking sammen

Lag et program som leser inn en liste med elevnavn, sorterer den alfabetisk med innsettingssortering, og lar brukeren søke etter et navn med binærsøk.

def innsettingssortering(liste):
    for i in range(1, len(liste)):
        nokkel = liste[i]
        j = i - 1
        while j >= 0 and liste[j].lower() > nokkel.lower():
            liste[j + 1] = liste[j]
            j -= 1
        liste[j + 1] = nokkel
    return liste

def binaersok(sortert_liste, sokeord):
    lav = 0
    hoy = len(sortert_liste) - 1

    while lav <= hoy:
        midt = (lav + hoy) // 2
        if sortert_liste[midt].lower() == sokeord.lower():
            return midt
        elif sortert_liste[midt].lower() < sokeord.lower():
            lav = midt + 1
        else:
            hoy = midt - 1
    return -1

# Les inn elevnavn
elever = ["Sara", "Ole", "Anna", "Erik", "Lise", "Kari", "Per"]
print(f"Original: {elever}")

# Sorter
innsettingssortering(elever)
print(f"Sortert:  {elever}")

# Søk
navn = input("\nSøk etter navn: ")
pos = binaersok(elever, navn)
if pos != -1:
    print(f"Fant {elever[pos]} på posisjon {pos + 1}")
else:
    print(f"{navn} finnes ikke i listen")

Kjøring:

Original: ['Sara', 'Ole', 'Anna', 'Erik', 'Lise', 'Kari', 'Per']
Sortert:  ['Anna', 'Erik', 'Kari', 'Lise', 'Ole', 'Per', 'Sara']

Søk etter navn: Kari
Fant Kari på posisjon 3

Vi bruker .lower() i sammenligningene for å gjøre sorteringen og søket uavhengig av store/små bokstaver.

📝Oppgave 3.4.1

Hva er hovedforskjellen mellom lineært søk og binærsøk?

📝Oppgave 3.4.2

Hvor mange sammenligninger trenger binærsøk maksimalt for å finne et element i en sortert liste med 1024 elementer?

📝Oppgave 3.4.3

I boblesortering, hva skjer i den første gjennomgangen av listen [5, 3, 8, 1, 4]?

📝Oppgave 3.4.4

Gå gjennom binærsøk steg for steg for å finne tallet 13 i listen [2, 5, 8, 13, 17, 21, 25, 30]. Skriv ned verdiene for lav, hoy og midt i hvert steg.

📝Oppgave 3.4.5

Implementer en funksjon i Python som bruker utvalgssortering til å sortere en liste med ordbøker etter en gitt nøkkel. For eksempel skal listen [{"navn": "Sara", "alder": 17}, {"navn": "Ole", "alder": 16}] kunne sorteres etter "alder" eller "navn".

📝Oppgave 3.4.6

Skriv et Python-program som sammenligner hastigheten til lineært søk og binærsøk. Programmet skal lage en sortert liste med 100 000 tall, søke etter et bestemt tall med begge metoder, og telle antall sammenligninger hver metode gjør.

📝Oppgave 3.4.7

Hvilken sorteringsalgoritme er mest effektiv for en liste som allerede er nesten sortert (f.eks. bare 2-3 elementer er på feil plass)?

Oppsummering

I dette kapittelet har du lært:

- Lineært søk: sjekker hvert element i rekkefølge.
- Binærsøk: halverer søkeområdet, krever sortert liste.
- Boblesortering: bytter naboelementer gjentatte ganger.
- Innsettings- og utvalgssortering: andre enkle sorteringsalgoritmer.
- Sammenligning: binærsøk er mye raskere enn lineært på store lister.

Noekkelbegreper


BegrepForklaring
Lineært søkSjekker elementene ett etter ett
BinærsøkHalverer søkeområdet i sortert liste
BoblesorteringSortering ved å bytte naboelementer

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.