DSA Interview Prep · Lektion

Bubble sort och insertion sort

Koda båda kvadratiska sorteringsalgoritmerna, förstå varför de är O(n²) och identifiera det fall där insertion sort är bättre än merge sort.

Lektion 1 av 413 steg

Bubble sort och insertion sort är en gratis lektion i DSA Interview Prep på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för DSA Interview Prep, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i DSA Interview Prep innehåller totalt 4 lektioner.

Varför studera O(n²)-sortering?

Bubblesortering och insättningssortering har O(n²) i värsta fall, vilket gör dem opraktiska för stora indata. Ändå förväntar sig varje seriös algoritmintervju att ni kan implementera och analysera dem. De lär ut grundläggande begrepp — jämförelse, byte, stabil sortering och beteende i bästa fall — som är tillämpliga på mer avancerade algoritmer. Intervjuare använder dem för att testa om ni kan resonera om loopinvarianter och asymptotisk notation från grunden.

# When O(n^2) is acceptable:
# n <= 1000: 10^6 ops, runs in milliseconds
# nearly-sorted data: insertion sort beats merge sort
# constant factor so small (simple ops) that overhead matters

import time

def time_sort(sort_fn, data):
    import copy
    arr = copy.copy(data)
    t = time.perf_counter()
    sort_fn(arr)
    return time.perf_counter() - t

print('Small n: quadratic sorts are fine')

Bubblesortering: låt maximum bubbla upp

Bubblesortering genomsöker arrayen upprepade gånger och byter plats på intilliggande element som ligger i fel ordning. Efter varje fullständig genomgång ”bubblar” det största osorterade elementet upp till sin slutliga plats i slutet. Efter n-1 genomgångar är hela arrayen sorterad. Namnet kommer från hur större element flyter uppåt likt bubblor. Det är den enklaste sorteringsalgoritmen att beskriva, men används sällan i praktiken.

def bubble_sort(arr):
    n = len(arr)
    for i in range(n - 1):          # n-1 passes
        for j in range(n - 1 - i):  # inner loop shrinks
            if arr[j] > arr[j+1]:   # out of order
                arr[j], arr[j+1] = arr[j+1], arr[j]  # swap
    return arr

arr = [64, 34, 25, 12, 22, 11, 90]
bubble_sort(arr)
print(arr)  # [11, 12, 22, 25, 34, 64, 90]

Bubblesortering med tidigt avbrott

En optimerad bubblesortering använder en flagga, swapped: om en fullständig inre genomgång inte utför några byten är arrayen redan sorterad och vi avslutar tidigt. Detta ger O(n) i bästa fall för redan sorterad indata — bubblesorteringens enda verkliga fördel. Utan denna flagga utförs alltid O(n²) jämförelser. Optimeringen med tidigt avbrott är vad intervjuare kontrollerar när de frågar om förbättringar av bubblesortering.

def bubble_sort_optimised(arr):
    n = len(arr)
    for i in range(n - 1):
        swapped = False
        for j in range(n - 1 - i):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = True
        if not swapped:  # already sorted!
            print(f'Sorted after pass {i+1}')
            break

arr1 = [1, 2, 3, 4, 5]  # already sorted
bubble_sort_optimised(arr1)  # exits after 1 pass

Komplexitetsanalys av bubblesortering

Bubblesorteringens yttre loop körs n-1 gånger. Den inre loopen körs n-1-i gånger per genomgång: (n-1) + (n-2) + ... + 1 = n(n-1)/2 ≈ n²/2 jämförelser. Detta ger O(n²) i genomsnitt och i värsta fall. Med flaggan för tidigt avbrott sjunker bästa fallet till O(n) för sorterad indata. Minneskomplexiteten är O(1) — endast bytet kräver ett tillfälligt byte av variabler. Bubblesortering är stabil: lika element behåller sin inbördes ordning eftersom vi endast byter element som är strikt större.

def bubble_sort_counted(arr):
    n = len(arr)
    swaps = comparisons = 0
    for i in range(n-1):
        for j in range(n-1-i):
            comparisons += 1
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swaps += 1
    return comparisons, swaps

arr = [5, 4, 3, 2, 1]  # worst case: reversed
c, s = bubble_sort_counted(arr)
print(f'Comparisons: {c}, Swaps: {s}')  # 10, 10 for n=5

Insättningssortering: bygga en sorterad hand

Insättningssortering efterliknar hur man sorterar en hand med kort: ta upp nästa kort (element) och sätt in det på rätt plats bland de redan sorterade korten till vänster. Invarianten är att arr[0:i] alltid är sorterad. För varje nytt element förskjuter ni större element åt höger för att skapa plats. Den här stabila algoritmen, som arbetar på plats, har O(n²) i värsta fall men O(n) i bästa fall för nästan sorterade data.

def insertion_sort(arr):
    for i in range(1, len(arr)):  # start from second element
        key = arr[i]              # element to insert
        j = i - 1
        # Shift larger elements to the right
        while j >= 0 and arr[j] > key:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = key            # insert in correct position
    return arr

arr = [12, 11, 13, 5, 6]
insertion_sort(arr)
print(arr)  # [5, 6, 11, 12, 13]

Insättningssortering steg för steg

Följ insättningssortering på [3, 1, 4, 2]: i=1, key=1, förskjut 3 åt höger → [1, 3, 4, 2]. i=2, key=4, inga förskjutningar → oförändrad. i=3, key=2, förskjut först 4 och sedan 3 åt höger → [1, 2, 3, 4]. Varje element jämförs med elementen till vänster tills rätt plats hittas. Den inre while-loopen utför förskjutningarna med tilldelningar (snabbare än byten eftersom en tilldelning krävs per förskjutning, jämfört med tre för ett byte).

def insertion_sort_trace(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j+1] = arr[j]  # shift right (1 assignment)
            j -= 1
        arr[j+1] = key
        print(f'After inserting {key}: {arr}')

insertion_sort_trace([3, 1, 4, 2])
# After inserting 1: [1, 3, 4, 2]
# After inserting 4: [1, 3, 4, 2]  (no change)
# After inserting 2: [1, 2, 3, 4]

Insättningssortering på nästan sorterade data

Insättningssorteringens stora styrka är komplexiteten O(n + inversions). En inversion är ett par (i,j) där i < j men arr[i] > arr[j]. För nästan sorterade arrayer med endast några få inversioner är insättningssortering extremt snabb — ibland snabbare än mergesortering i praktiken tack vare sin enkelhet och cachevänliga åtkomstmönster. Pythons Timsort använder insättningssortering på små delarrayer av just den anledningen.

# Nearly sorted: only 1 inversion
arr1 = [1, 2, 4, 3, 5]  # 4>3 is the only inversion

def count_ops(arr):
    arr = arr[:]
    ops = 0
    for i in range(1, len(arr)):
        key = arr[i]; j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j+1] = arr[j]; j -= 1; ops += 1
        arr[j+1] = key
    return ops

print(count_ops([1,2,4,3,5]))  # 1 op (nearly sorted)
print(count_ops([5,4,3,2,1]))  # 10 ops (reversed = worst case)

Stabilitet vid sortering

En sorteringsalgoritm är stabil om lika element behåller sin ursprungliga inbördes ordning efter sorteringen. Både bubblesortering och insättningssortering är stabila — de byter aldrig plats på lika element. Stabilitet är viktig när ni sorterar efter flera nycklar i följd: sortera först efter den sekundära nyckeln (stabilt) och sedan efter den primära nyckeln (stabilt), så att ordningen efter den sekundära nyckeln bevaras bland element med samma primärnyckel. Mergesortering är också stabil; heapsortering och quicksort är vanligtvis inte det.

# Stable sort preserves order of equal elements
students = [
    ('Alice', 85),
    ('Bob',   92),
    ('Carol', 85),
    ('Dave',  78),
]
# Sort by score ascending (stable: Alice before Carol for same score)
students.sort(key=lambda x: x[1])
for s in students:
    print(s)
# ('Dave',78) ('Alice',85) ('Carol',85) ('Bob',92)
# Alice still comes before Carol  => stable

Insättningssortering med binär sökning

Insättningssorteringens inre loop hittar både rätt position och förskjuter element. Ni kan använda binär sökning för att hitta positionen med O(log i) jämförelser, men förskjutningarna tar fortfarande O(i) tid — den övergripande komplexiteten förblir därför O(n²). Optimeringen minskar antalet jämförelser (användbart för dyra jämförelsefunktioner), men inte det totala antalet operationer. Den här varianten, binär insättningssortering, förekommer i Timsort för små delstorlekar.

import bisect

def binary_insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        # Find insertion point in O(log i)
        pos = bisect.bisect_left(arr, key, 0, i)
        # Shift elements to make room: still O(i)
        arr[pos+1:i+1] = arr[pos:i]
        arr[pos] = key
    return arr

print(binary_insertion_sort([5, 2, 4, 6, 1, 3]))
# [1, 2, 3, 4, 5, 6]

Bubblesortering eller insättningssortering: när används de?

I intervjuer bör ni ange jämförelsen med säkerhet: insättningssortering är strikt bättre än bubblesortering — båda har O(n²) i värsta fall och O(1) minne, men insättningssortering utför färre skrivningar (O(n+k) för k inversioner jämfört med O(n²) för bubblesortering), är mer cachevänlig och är det praktiska valet för små n (Timsort använder den). Bubblesorteringens enda verkliga fördel är pedagogisk enkelhet. I produktion bör ni alltid använda språkets inbyggda sortering.

# Summary: when to use quadratic sorts
# Use insertion_sort when:
#   - n <= 20 (small enough that O(n^2) is fine)
#   - data is nearly sorted (few inversions => fast)
#   - you need stable sort with O(1) space
#   - implementing a hybrid (like Timsort)

# NEVER use bubble_sort in production code
# Python's built-in sort: O(n log n), stable, extremely fast
arr = [5, 2, 8, 1, 9]
print(sorted(arr))   # [1, 2, 5, 8, 9]
arr.sort()
print(arr)           # [1, 2, 5, 8, 9]

Att räkna inversioner som mått

Antalet inversioner i en array är lika med antalet par (i,j) där i < j men arr[i] > arr[j]. Insättningssortering utför exakt lika många förskjutningar som antalet inversioner — en användbar insikt. För att räkna inversioner effektivt (O(n log n)) krävs en modifierad mergesortering. Intervjuare frågar ibland ”hur väl tar er algoritm hänsyn till inversioner?” som en följdfråga vid diskussioner om sortering.

# Count inversions: naive O(n^2)
def count_inversions_naive(arr):
    count = 0
    for i in range(len(arr)):
        for j in range(i+1, len(arr)):
            if arr[i] > arr[j]:
                count += 1
    return count

print(count_inversions_naive([3, 1, 2]))  # 2: (3,1) and (3,2)
print(count_inversions_naive([1, 2, 3]))  # 0: already sorted
print(count_inversions_naive([3, 2, 1]))  # 3: all pairs inverted

Snabbtest

Testa er förståelse av begreppen Data Structures & Algorithms — Coding Interview Prep från den här lektionen.

Sammanfattning av lektionen

I den här lektionen har ni lärt er: bubblesortering utför n-1 genomgångar, där varje genomgång låter det aktuella maximumet bubbla upp till sin slutliga plats, med O(n²) i värsta fall men O(n) i bästa fall med flaggan för tidigt avbrott, insättningssortering förskjuter element åt höger för att sätta in den aktuella nyckeln på rätt sorterad plats och körs på O(n + inversions) tid, vilket gör den optimal för nästan sorterade data och båda algoritmerna är stabila, använder O(1) minne och har O(n²) i värsta fall — men insättningssortering föredras strikt framför bubblesortering i alla praktiska situationer. Härnäst implementerar vi mergesortering från grunden.

Gratis att börja

Lär dig Python med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”Bubble sort och insertion sort” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen DSA Interview Prep, inklusive ”Bubble sort och insertion sort”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i DSA Interview Prep innehåller totalt 4 lektioner.

Vad lär jag mig i ”Bubble sort och insertion sort”?

Koda båda kvadratiska sorteringsalgoritmerna, förstå varför de är O(n²) och identifiera det fall där insertion sort är bättre än merge sort. Ni övar på DSA Interview Prep med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig DSA Interview Prep?

Du behöver inga förkunskaper. Utbildningen i DSA Interview Prep på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Bubble sort och insertion sort”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här DSA Interview Prep-lektionen?

Ja. Varje DSA Interview Prep-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Bubble sort och insertion sort
  2. Merge sort: dela, sortera, sammanfoga
  3. Quick sort och pivotval
  4. Icke-jämförande sorteringar och Pythons sort()
← Tillbaka till DSA Interview Prep