Förberedelse inför kodningsintervjuer · Lektion

Big-O-notation från grunden

Förstå varför asymptotisk tillväxt är viktig, hur konstanter och termer av lägre ordning utelämnas och hur ni snabbt tolkar Big-O.

Lektion 1 av 413 steg

Big-O-notation från grunden är en gratis lektion i Förberedelse inför kodningsintervjuer på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Förberedelse inför kodningsintervjuer, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Förberedelse inför kodningsintervjuer innehåller totalt 4 lektioner.

Varför mäta algoritmers effektivitet

Två program kan båda vara korrekta, men det ena blir klart på ett ögonblick medan det andra körs i timmar. Tidskomplexitet beskriver hur körtiden växer när indata blir större.

# O(n) approach
def find_max_linear(nums):
    m = nums[0]
    for n in nums:
        if n > m: m = n
    return m

# O(n^2) approach (unnecessary double loop)
def find_max_quadratic(nums):
    for i in range(len(nums)):
        is_max = all(nums[i] >= nums[j] for j in range(len(nums)))
        if is_max: return nums[i]

print(find_max_linear([3, 1, 4, 1, 5, 9]))  # 9

Big-O: asymptotisk övre gräns

Big-O beskriver den värsta fallens övre gräns för hur snabbt kostnaden växer. Tricket är att ta bort konstanter och mindre termer, eftersom bara den dominerande termen spelar roll i stor skala. Se koden.

# T(n) = 3n^2 + 5n + 100 is O(n^2)
# because the n^2 term dominates for large n

# T(n) = 2n + 1000 is O(n)
# the constant 1000 becomes negligible

# Rule: drop constants and lower-order terms
# 5n^3 + 2n^2 + n + 1  =>  O(n^3)
# 100 * log(n) + n      =>  O(n)
print('O(n^2) example: counting iterations')
n = 1000
count = sum(1 for i in range(n) for j in range(n))
print(count)  # 1_000_000 = n^2

Vanliga komplexitetsklasser

Från snabbast till långsammast: O(1), O(log n), O(n), O(n log n), O(n^2), O(2^n), O(n!). Om ni känner till dessa kan ni välja rätt metod innan ni skriver en enda rad.

import math

n = 1000
print(f'O(1):       {1}')
print(f'O(log n):   {int(math.log2(n))}')
print(f'O(n):       {n}')
print(f'O(n log n): {int(n * math.log2(n))}')
print(f'O(n^2):     {n**2}')
# O(2^n) for n=1000 is astronomically large
# O(n!) even larger

Att utelämna konstanter: varför det är viktigt

Att köra 5n steg eller 2n steg ger i båda fallen O(n) — konstanter beror på hårdvaran, inte algoritmen. Big-O utelämnar dem så att du kan jämföra skalningen på samma villkor.

# Both are O(n) — different constants
def count_a(n):
    total = 0
    for i in range(n):   # n ops
        total += 1
    for i in range(n):   # n ops
        total += 1
    return total  # T(n) = 2n  =>  O(n)

def count_b(n):
    total = 0
    for i in range(5 * n):  # 5n ops
        total += 1
    return total  # T(n) = 5n  =>  O(n)

print(count_a(10), count_b(10))  # 20 50

Bästa, genomsnittliga och värsta fall

Big-O beskriver värsta fallet; Omega beskriver det bästa fallet; Theta är en snäv gräns för båda. När en intervjuare frågar efter "komplexiteten" menar de nästan alltid värsta fallet.

def linear_search(nums, target):
    for i, n in enumerate(nums):
        if n == target:
            return i  # best case: target at index 0 => O(1)
    return -1         # worst case: not found => O(n)

# Best case O(1): target is first element
print(linear_search([5,1,2,3], 5))   # 0

# Worst case O(n): target not in list
print(linear_search([1,2,3,4], 9))   # -1

O(log n): halvera sökutrymmet

En algoritm är O(log n) när den halverar indata i varje steg, som vid binärsökning. Även för en miljard element behövs bara omkring 30 steg — otroligt snabbt. Se koden.

def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    steps = 0
    while lo <= hi:
        steps += 1
        mid = (lo + hi) // 2
        if arr[mid] == target:
            return mid, steps
        elif arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1, steps

import math
arr = list(range(1000))
idx, s = binary_search(arr, 999)
print(f'Found at {idx} in {s} steps (log2(1000)~={math.log2(1000):.1f})')

O(n log n): undre gränsen för sortering

Varje jämförelsebaserad sortering behöver minst O(n log n) i värsta fallet — det är en verklig matematisk undre gräns. Därför har sortera-och-skanna totalt komplexiteten O(n log n), inte O(n^2). Koden visar mergesortering.

# Merge sort: O(n log n)
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left  = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(a, b):
    res, i, j = [], 0, 0
    while i < len(a) and j < len(b):
        if a[i] <= b[j]: res.append(a[i]); i+=1
        else:             res.append(b[j]); j+=1
    return res + a[i:] + b[j:]

print(merge_sort([5,2,8,1,9,3]))  # [1,2,3,5,8,9]

Amortiserad komplexitet

Amortiserad analys beräknar den genomsnittliga kostnaden över många operationer. Pythons append har amortiserad komplexitet O(1): det går vanligtvis direkt, medan den sällsynta storleksändringen på O(n) fördelas tunt över alla anrop till append.

# Dynamic array append is O(1) amortised
import sys

lst = []
capacities = []
for i in range(16):
    lst.append(i)
    capacities.append(sys.getsizeof(lst))

# Size jumps show reallocation events
for i, c in enumerate(capacities):
    if i > 0 and capacities[i] != capacities[i-1]:
        print(f'Realloc at i={i}, new size={c} bytes')

Känna igen komplexitet i kod

En snabb tumregel: räkna loopar. En loop är O(n), två nästlade loopar är O(n^2), och en loop som halverar är O(log n). Oberoende genomgångar adderas; endast nästlade loopar multipliceras. Se koden.

# Two independent passes: O(n) + O(n) = O(n)
def two_passes(nums):
    total = sum(nums)           # O(n)
    mean = total / len(nums)
    diffs = [abs(n - mean) for n in nums]  # O(n)
    return max(diffs)           # O(n)
# Overall: O(n) -- NOT O(n^2)

# Nested loops: O(n) * O(n) = O(n^2)
def all_pairs(nums):
    pairs = []
    for i in range(len(nums)):       # O(n)
        for j in range(i+1, len(nums)): # O(n)
            pairs.append((nums[i], nums[j]))
    return pairs  # O(n^2)

Grunderna i minneskomplexitet

Minneskomplexitet mäter det extra minne du använder utöver indata. En vändning på plats är O(1); en hash map är O(n). När du byter tid mot minne ska du alltid ange båda.

# O(1) space: reverse in-place
def reverse_inplace(arr):
    l, r = 0, len(arr) - 1
    while l < r:
        arr[l], arr[r] = arr[r], arr[l]
        l += 1; r -= 1

# O(n) space: create reversed copy
def reverse_copy(arr):
    return arr[::-1]

a = [1, 2, 3, 4, 5]
reverse_inplace(a)
print(a)  # [5, 4, 3, 2, 1]

Prata om komplexitet på intervjuer

Ange alltid komplexiteten utan att bli ombedd: "Det här tar O(n log n) tid och använder O(n) minne." Erbjud sedan ett snabbare alternativ. Den vanan signalerar verklig senioritet.

# Example of explaining complexity step by step
def two_sum(nums, target):
    # O(n) time: one pass through nums
    # O(n) space: hash map stores up to n elements
    seen = {}  # value -> index
    for i, n in enumerate(nums):
        complement = target - n
        if complement in seen:   # O(1) lookup
            return [seen[complement], i]
        seen[n] = i
    return []

print(two_sum([2, 7, 11, 15], 9))  # [0, 1]

Snabbtest

Snabbtest — visa vad du har lärt dig om Big-O och komplexitetsklasser. En fråga, det här klarar du. 🎯

Lektionssammanfattning

Sammanfattning: Big-O beskriver tillväxten i värsta fallet med utelämnade konstanter, du känner till klasserna från O(1) till O(n!), och oberoende loopar adderas medan nästlade loopar multipliceras.

Gratis att börja

Lär dig Förberedelse inför kodningsintervjuer 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
90
Lektioner
360

Vanliga frågor

Är lektionen ”Big-O-notation från grunden” gratis?

Ja – hela texten till ”Big-O-notation från grunden” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Förberedelse inför kodningsintervjuer, kan Ni uppgradera till CoddyKit PRO. Kursen i Förberedelse inför kodningsintervjuer innehåller totalt 4 lektioner.

Vad lär jag mig i ”Big-O-notation från grunden”?

Förstå varför asymptotisk tillväxt är viktig, hur konstanter och termer av lägre ordning utelämnas och hur ni snabbt tolkar Big-O. Ni övar på Förberedelse inför kodningsintervjuer 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 Förberedelse inför kodningsintervjuer?

Du behöver inga förkunskaper. Utbildningen i Förberedelse inför kodningsintervjuer 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 ”Big-O-notation från grunden”?

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 Förberedelse inför kodningsintervjuer-lektionen?

Ja. Varje Förberedelse inför kodningsintervjuer-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. Big-O-notation från grunden
  2. Analysera loopar och nästlade loopar
  3. Rekursion och metoden med rekursionsträd
  4. Rymdkomplexitet och avvägningar
← Tillbaka till Förberedelse inför kodningsintervjuer