Bubble sort og insertion sort
Kod begge kvadratiske sorteringsalgoritmer, forstå hvorfor de er O(n²), og genkend det ene tilfælde, hvor insertion sort slår merge sort.
Bubble sort og insertion sort er en gratis Forberedelse til kodeinterviews-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Forberedelse til kodeinterviews, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.
Hvorfor studere O(n²)-sorteringer?
Boblesortering og indsættelsessortering tager O(n²) i værste fald, så de er upraktiske til store inddata. Alligevel forventer ethvert seriøst algoritmeinterview, at du kan implementere og analysere dem. De lærer dig grundlæggende begreber — sammenligning, ombytning, stabil sortering og adfærd i bedste fald — som også gælder for mere avancerede algoritmer. Interviewere bruger dem til at afprøve, om du kan ræsonnere ud fra løkkeinvarianter og asymptotisk notation helt fra bunden.
# 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')Boblesortering: Flyt maksimum op
Boblesortering gennemløber gentagne gange arrayet og bytter naboelementer, der står i forkert rækkefølge. Efter hvert fulde gennemløb "bobler" det største usorterede element op til sin endelige position til sidst. Efter n-1 gennemløb er hele arrayet sorteret. Navnet kommer fra den måde, større elementer flyder opad på som bobler. Det er den enkleste sorteringsalgoritme at beskrive, men bruges sjældent i praksis.
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]Boblesortering med tidlig afslutning
En optimeret boblesortering bruger et swapped-flag: Hvis et helt indre gennemløb ikke foretager nogen ombytninger, er arrayet allerede sorteret, og vi afslutter tidligt. Det giver O(n) i bedste fald for allerede sorteret inddata — boblesorteringens eneste reelle fordel. Uden dette flag udfører den altid O(n²) sammenligninger. Optimeringen med tidlig afslutning er det, interviewere ser efter, når de spørger til forbedringer af boblesortering.
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 passAnalyse af boblesorteringens kompleksitet
Boblesorteringens ydre løkke kører n-1 gange. Den indre løkke kører n-1-i gange pr. gennemløb: (n-1) + (n-2) + ... + 1 = n(n-1)/2 ≈ n²/2 sammenligninger. Det giver O(n²) i gennemsnit og i værste fald. Med flaget til tidlig afslutning falder bedste fald til O(n) for sorteret inddata. Pladskompleksiteten er O(1) — kun ombytningen kræver en midlertidig variabel. Boblesortering er stabil: Ens elementer bevarer deres indbyrdes rækkefølge, fordi vi kun bytter strengt større elementer.
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=5Indsættelsessortering: Opbygning af en sorteret hånd
Indsættelsessortering efterligner sortering af en hånd kort: Tag det næste kort, altså elementet, og indsæt det på den korrekte position blandt de allerede sorterede kort til venstre. Invarianten er, at arr[0:i] altid er sorteret. For hvert nyt element skal du forskyde større elementer mod højre for at skabe plads. Denne algoritme sorterer på stedet, er stabil, tager O(n²) i værste fald og O(n) i bedste fald for næsten sorterede 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]Indsættelsessortering trin for trin
Følg indsættelsessortering på [3, 1, 4, 2]: i=1, key=1, forskyd 3 mod højre → [1, 3, 4, 2]. i=2, key=4, ingen forskydninger → uændret. i=3, key=2, forskyd først 4 og derefter 3 mod højre → [1, 2, 3, 4]. Hvert element sammenlignes med elementerne til venstre, indtil vi finder den korrekte plads. Den indre while-løkke udfører forskydningerne med tildelinger, hvilket er hurtigere end ombytninger, fordi hver forskydning kræver én tildeling mod tre ved en ombytning.
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]Indsættelsessortering på næsten sorterede data
Indsættelsessorteringens afgørende fordel er kompleksiteten O(n + inversioner). En inversion er et par (i,j), hvor i < j, men arr[i] > arr[j]. For næsten sorterede arrays med kun få inversioner er indsættelsessortering ekstremt hurtig — nogle gange hurtigere end flettesortering i praksis på grund af sin enkelhed og cachevenlige adgangsmåde. Pythons Timsort bruger indsættelsessortering på små delarrays af netop denne grund.
# 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 i sortering
En sorteringsalgoritme er stabil, hvis ens elementer bevarer deres oprindelige indbyrdes rækkefølge efter sorteringen. Både boblesortering og indsættelsessortering er stabile — de bytter aldrig ens elementer. Stabilitet er vigtig, når du sorterer efter flere nøgler i rækkefølge: Sortér først stabilt efter den sekundære nøgle, og sortér derefter stabilt efter den primære nøgle, så rækkefølgen efter den sekundære nøgle bevares blandt elementer med samme primære nøgle. Flettesortering er også stabil; heapsort og quicksort er som regel ikke.
# 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 => stableIndsættelsessortering som binær søgning
Indsættelsessorteringens indre løkke finder både den korrekte position og forskyder elementerne. Du kan bruge binær søgning til at finde positionen på O(log i) sammenligninger, men forskydningerne tager stadig O(i) tid — så den samlede kompleksitet forbliver O(n²). Optimeringen reducerer antallet af sammenligninger, hvilket er nyttigt ved dyre sammenligningsfunktioner, men ikke antallet af samlede operationer. Denne "binære indsættelsessortering" optræder i Timsort ved små blokstørrelser.
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]Boblesortering kontra indsættelsessortering: Hvornår skal du bruge hver af dem?
I interviews skal du fremsætte denne sammenligning med sikkerhed: indsættelsessortering er entydigt bedre end boblesortering — begge tager O(n²) i værste fald og bruger O(1) plads, men indsættelsessortering udfører færre skrivninger (O(n+k) for k inversioner mod O(n²) for boblesortering), er mere cachevenlig og er det praktiske valg for lille n, som Timsort også bruger. Boblesorteringens eneste reelle fordel er pædagogisk enkelhed. I produktionskode skal du altid bruge sprogets indbyggede 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]Optælling af inversioner som mål
Antallet af inversioner i et array er lig med antallet af par (i,j), hvor i < j, men arr[i] > arr[j]. Indsættelsessortering udfører præcis lige så mange forskydninger, som der er inversioner — en nyttig indsigt. Effektiv optælling af inversioner (O(n log n)) kræver en ændret flettesortering. Interviewere spørger nogle gange: 'Hvor godt tager din algoritme højde for inversioner?' som opfølgning på en samtale 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 invertedHurtig kontrol
Afprøv din forståelse af begreberne fra lektionen Data Structures & Algorithms — Coding Interview Prep.
Opsummering af lektionen
I denne lektion lærte du, at boblesortering foretager n-1 gennemløb, hvor hvert gennemløb får det aktuelle maksimum på plads, med O(n²) i værste fald, men O(n) i bedste fald med flaget til tidlig afslutning, at indsættelsessortering forskyder elementer mod højre for at indsætte den aktuelle nøgle på den korrekte sorterede position og kører på O(n + inversioner), hvilket gør den optimal til næsten sorterede data, og at begge algoritmer er stabile, bruger O(1) plads og tager O(n²) i værste fald — men indsættelsessortering bør entydigt foretrækkes frem for boblesortering i alle praktiske situationer. Som det næste implementerer vi flettesortering fra bunden.
Lær Forberedelse til kodeinterviews med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 90
- Lektioner
- 360
Ofte stillede spørgsmål
Er lektionen “Bubble sort og insertion sort” gratis?
Ja — hele teksten til “Bubble sort og insertion sort” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Forberedelse til kodeinterviews-kurset, skal du opgradere til CoddyKit PRO. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Bubble sort og insertion sort”?
Kod begge kvadratiske sorteringsalgoritmer, forstå hvorfor de er O(n²), og genkend det ene tilfælde, hvor insertion sort slår merge sort. Du øver dig i Forberedelse til kodeinterviews med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Forberedelse til kodeinterviews?
Der kræves ingen tidligere erfaring. Forberedelse til kodeinterviews på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.
Hvor lang tid tager lektionen “Bubble sort og insertion sort”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Forberedelse til kodeinterviews-lektion?
Ja. Alle Forberedelse til kodeinterviews-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Bubble sort og insertion sort
- Merge sort: opdel, sortér, flet
- Quick sort og valg af pivot
- Sortering uden sammenligning og Pythons sort()