Programmering nivå 2

Kap 2.3 – Sökning och sortering

Hitta, ordna och jämför data i listor med algoritmer och inbyggda metoder.

Mål med lektionen

När du har arbetat klart med denna lektion ska du:

  • Förstå hur man söker efter ett värde i en lista.
  • Känna till skillnaden mellan linjär och binär sökning.
  • Känna till hur enkla sorteringsalgoritmer fungerar på en övergripande nivå.
  • Kunna sortera listor med Pythons inbyggda metoder sort() och sorted().
  • Förstå när olika typer av sökning och sortering är användbara, utan krav på att skriva egna sök- eller sorteringsalgoritmer från grunden.

Så här lär du dig bäst

Börja med att testa exempel med små listor och skriv gärna ut varje steg. Analysera hur många steg som krävs vid olika metoder. Målet är att känna till hur några vanliga sök- och sorteringsalgoritmer fungerar, men i egna program ska du i första hand använda Pythons inbyggda verktyg.

Sökning i listor

Sökning betyder att programmet letar efter ett värde i en samling data. Resultatet kan till exempel vara indexet där värdet finns, eller -1 om värdet saknas.

Linjär sökning

Linjär sökning går igenom varje element i listan tills rätt värde hittas.

def linear_search(lst, target):
    for i in range(len(lst)):
        if lst[i] == target:
            return i

    return -1

names = ["Anna", "Bo", "Sara"]
print(linear_search(names, "Bo"))  # 1

Fördel: fungerar alltid. Nackdel: kan bli långsam för stora listor.

Steg för steg: så fungerar koden

  1. def linear_search(lst, target): skapar en funktion som tar emot två saker: en lista och värdet vi letar efter.
  2. for i in range(len(lst)): går igenom listans index från 0 till sista positionen. Om listan har tre element blir indexen 0, 1 och 2.
  3. if lst[i] == target: jämför elementet på den aktuella positionen med det vi söker efter.
  4. Om värdet hittas körs return i. Funktionen skickar då tillbaka indexet och avslutas direkt.
  5. Om loopen hinner gå igenom hela listan utan träff körs return -1. Det betyder att värdet inte finns i listan.

I exemplet är listan ["Anna", "Bo", "Sara"] och vi söker efter "Bo". Programmet jämför först "Anna" på index 0. Det är fel. Sedan jämför det "Bo" på index 1. Det är rätt, därför returnerar funktionen 1.

Interaktiv övning: linjär sökning

Testa hur linjär sökning jämför ett namn i taget tills målet hittas eller listan tar slut.

Prova till exempel att:

  • söka efter Bo och se att sökningen stannar på index 1
  • söka efter Sara och se att fler jämförelser behövs
  • söka efter ett namn som inte finns
Tryck på Kör sökning för att testa algoritmen.

Binär sökning

Binär sökning är mycket snabbare, men kräver att listan är sorterad.

Viktigt: Binär sökning fungerar inte korrekt på en osorterad lista. Om listan inte är sorterad ska du antingen sortera den först eller använda linjär sökning.

def binary_search(lst, target):
    low = 0
    high = len(lst) - 1

    while low <= high:
        mid = (low + high) // 2

        if lst[mid] == target:
            return mid
        elif lst[mid] < target:
            low = mid + 1
        else:
            high = mid - 1

    return -1

numbers = [3, 6, 9, 12, 15]
print(binary_search(numbers, 12))  # 3

Binär sökning halverar sökområdet varje gång, vilket gör metoden effektiv för stora sorterade listor.

Steg för steg: så fungerar binär sökning

  1. Listan måste först vara sorterad. I exemplet är listan [3, 6, 9, 12, 15].
  2. low = 0 betyder att sökområdet börjar vid första indexet i listan.
  3. high = len(lst) - 1 betyder att sökområdet slutar vid sista indexet.
  4. while low <= high: fortsätter söka så länge det finns ett möjligt område kvar.
  5. mid = (low + high) // 2 räknar ut mittenpositionen i det aktuella sökområdet.
  6. Om lst[mid] == target är värdet hittat och funktionen returnerar indexet mid.
  7. Om lst[mid] < target ligger målet till höger, eftersom listan är sorterad. Då flyttas low till mid + 1.
  8. Annars ligger målet till vänster. Då flyttas high till mid - 1.
  9. Om sökområdet tar slut utan träff returneras -1.

I exemplet söker programmet efter 12. Först tittar det på mittenvärdet 9. Eftersom 12 är större än 9 behöver programmet bara söka i högra delen av listan. Där hittar det 12 på index 3.

När ska du använda vilken sökning?

  • Använd linjär sökning när listan är liten eller inte sorterad.
  • Använd binär sökning när listan redan är sorterad och du behöver söka snabbt.
  • I vanliga Pythonprogram använder du ofta inbyggda verktyg först, men egna algoritmer hjälper dig att förstå vad som händer bakom kulisserna.

Sortering av listor

Inbyggd sortering

Python gör det enkelt att sortera listor:

sort() och sorted() använder Pythons inbyggda sortering. Den är snabb, stabil och optimerad för många vanliga typer av data. I riktiga program ska du därför nästan alltid använda sort() eller sorted() i stället för att skriva en egen sorteringsalgoritm från grunden. Läs mer i Pythons Sorting HOWTO.

Skillnaden är att sort() ändrar listan direkt, medan sorted() skapar en ny sorterad lista och lämnar originalet oförändrat.

numbers = [5, 3, 9, 1]
numbers.sort()  # Ändrar listan direkt
print(numbers)  # [1, 3, 5, 9]

names = ["Zara", "Adam", "Lilly"]
sorted_names = sorted(names)  # Skapar ny lista
print(sorted_names)

Interaktiv övning: inbyggd sortering

Testa skillnaden mellan sort() och sorted(). Den ena ändrar listan direkt, den andra skapar en ny sorterad lista.

Prova till exempel att:

  • sortera namn som Zara, Adam, Lilly
  • sortera tal som 5, 3, 9, 1
  • växla mellan sort() och sorted() och jämför originalet
Tryck på Kör sortering för att testa skillnaden.

Egen sortering: bubble sort

Bubble sort används mest i undervisning. Den visar principen för jämförelse och byte, som återkommer i många sorteringsalgoritmer.

I riktiga projekt använder du nästan alltid Pythons inbyggda sort() eller sorted(). Bubble sort är med här för att visa hur en sorteringsalgoritm kan byggas upp steg för steg.

def bubble_sort(lst):
    for i in range(len(lst)):
        for j in range(0, len(lst) - i - 1):
            if lst[j] > lst[j + 1]:
                lst[j], lst[j + 1] = lst[j + 1], lst[j]

    return lst

print(bubble_sort([9, 3, 7, 1]))  # [1, 3, 7, 9]

Interaktiv övning: bubble sort

Skriv en lista med tal och kör sorteringen. Resultatet visar jämförelser och byten i ordning.

Prova till exempel att:

  • använda 9, 3, 7, 1 och följa varje byte
  • använda en nästan sorterad lista, till exempel 1, 3, 2, 4
  • jämföra med Pythons inbyggda sort()
Tryck på Kör sortering för att testa algoritmen.

Öva själv

Övning 1: Sortera användarnamn med sort()

Skapa en lista med minst fem användarnamn i blandad ordning. Skriv ut listan, sortera den med sort() och skriv ut den igen.

Kontrollera programmet

  • Den första utskriften ska visa den ursprungliga ordningen.
  • Den andra utskriften ska visa namnen i sorterad ordning.
  • Testa även med en lista som redan är sorterad.

Fundera: Vad har hänt med originallistan efter sort()?

När du har försökt själv: visa lösningsförslaget till övning 1 på GitHub.

Övning 2: Skapa en sorterad kopia

Skapa en lista med tal i blandad ordning. Använd sorted() för att skapa en ny sorterad lista. Skriv ut både originallistan och den sorterade listan.

Testa programmet med

  • [8, 2, 10, 4].
  • en lista som redan är sorterad.
  • en lista som ligger i omvänd ordning.

Fundera: Varför kan sorted() passa bättre än sort() när du behöver behålla den ursprungliga ordningen?

När du har försökt själv: visa lösningsförslaget till övning 2 på GitHub.

Övning 3: Linjär sökning efter ett namn

Skriv funktionen linear_search(names, target). Funktionen ska gå igenom listans index och returnera indexet där namnet finns. Om namnet saknas ska funktionen returnera -1.

Testa funktionen med

  • ett namn som ligger först i listan.
  • ett namn som ligger sist i listan.
  • ett namn som inte finns.

Fundera: Varför ligger return -1 efter loopen och inte inuti den?

När du har försökt själv: visa lösningsförslaget till övning 3 på GitHub.

Övning 4: Sortera före binär sökning

Använd funktionen binary_search() från kapitlets exempel. Skapa först en osorterad lista med tal och gör en sorterad kopia med sorted(). Sök sedan efter ett tal i den sorterade kopian och skriv ut det returnerade indexet.

Testa programmet med

  • ett tal som ligger först i den sorterade listan.
  • ett tal som ligger sist i den sorterade listan.
  • ett tal som inte finns.

Fundera: Varför ska du inte skicka den osorterade listan direkt till binary_search()?

När du har försökt själv: visa lösningsförslaget till övning 4 på GitHub.

Övning 5: Läs in, sortera och sök

Svårare övning: Här kombinerar du funktioner, listor, säker inmatning, sortering och linjär sökning.

Skapa ett program som frågar efter fem heltal och lägger dem i en lista. Felaktig inmatning ska hanteras så att användaren får försöka igen. Skapa därefter en sorterad kopia med sorted(), fråga vilket tal användaren vill söka efter och använd en funktion för linjär sökning. Skriv ut det hittade indexet eller ett meddelande om talet saknas.

Testa programmet genom att

  • skriva text när programmet frågar efter ett tal.
  • söka efter ett tal som finns i listan.
  • köra programmet igen och söka efter ett tal som saknas.

Fundera: Söker programmet i originallistan eller den sorterade kopian, och hur påverkar det indexet som returneras?

När du har försökt själv: visa lösningsförslaget till övning 5 på GitHub.

Övning 6: Meny för användarnamn

Svårare övning: Här kombinerar du en lista med funktioner, en meny, sortering och sökning.

Skapa ett menyprogram med alternativen 1. Lägg till användarnamn, 2. Visa sorterade användarnamn, 3. Sök efter användarnamn och 4. Avsluta. Namnen ska sparas i en lista. Den sorterade utskriften ska göras från en kopia så att originallistan inte ändras. Sökningen ska använda en egen funktion för linjär sökning och skriva ut namnets index i originallistan eller att namnet saknas.

Testa programmet genom att

  • visa listan innan något namn har lagts till.
  • lägga till minst tre namn i blandad ordning.
  • visa namnen i sorterad ordning.
  • söka efter ett namn som finns och ett som saknas.
  • göra ett ogiltigt menyval och därefter avsluta.

Fundera: Varför används en sorterad kopia när namnen visas men originallistan när indexet söks?

När du har försökt själv: visa lösningsförslaget till övning 6 på GitHub.

Reflektion

  • Vilka fördelar har inbyggda metoder jämfört med att skriva egna algoritmer?
  • När är det värt att använda binär sökning istället för linjär?
  • Hur kan sortering göra andra delar av ett program mer effektiva?

Tillbaka till Kapitel 2