DSA Interview Prep · leksjon

Avveininger mellom rekursiv og iterativ løsning

Gjør rekursivt fakultet og Fibonacci om til iterative løkker, og forklar når Pythons rekursjonsgrense og stakkstørrelse gjør iterasjon til det beste valget.

Leksjon 3 av 413 trinn

Avveininger mellom rekursiv og iterativ løsning er en gratis leksjon i DSA Interview Prep på CoddyKit. Dette er leksjon 3 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i DSA Interview Prep, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i DSA Interview Prep inneholder totalt 4 leksjoner.

Dualiteten mellom rekursjon og iterasjon

Enhver algoritme som kan skrives rekursivt, kan også skrives iterativt, og omvendt. Den rekursive versjonen gjenspeiler ofte problemets matematiske definisjon mer direkte, mens den iterative versjonen gir eksplisitt kontroll over minnet og unngår risikoen for stakkoverflyt. Valget mellom dem er en pragmatisk avgjørelse basert på lesbarhet, dybdebegrensninger og ytelseskrav.

I intervjuer er det et sterkt tegn på mestring å kunne presentere begge versjonene og forklare avveiningene.

Fakultet: rekursivt kontra iterativt

Fakultet er det klassiske eksempelet. Den rekursive versjonen koder direkte den matematiske definisjonen n! = n × (n-1)!. Den bruker O(n) stakkplass på grunn av n ventende returverdier. Den iterative versjonen går i løkke fra 1 til n og bruker O(1) plass. For n = 1000 når den rekursive versjonen Pythons standardgrense, mens den iterative versjonen håndterer vilkårlig store n.

def factorial_rec(n):
    if n == 0:
        return 1
    return n * factorial_rec(n - 1)   # O(n) stack

def factorial_iter(n):
    result = 1
    for i in range(2, n + 1):
        result *= i                    # O(1) stack
    return result

print(factorial_rec(10))   # 3628800
print(factorial_iter(10))  # 3628800

# Large n: iterative works, recursive may overflow
print(factorial_iter(1000) > 0)  # True (Python handles big ints)

Fibonacci: eksponentiell kontra lineær

Naiv rekursiv Fibonacci har tidskompleksitet O(2^n) – den er katastrofalt treg for store n. Den iterative versjonen har tidskompleksitet O(n) og plasskompleksitet O(1). Memoisert rekursjon (i neste leksjon) har også tidskompleksitet O(n), men plasskompleksitet O(n) på grunn av memo-ordboken og O(n) stakkplass. For Fibonacci er den iterative tilnærmingen optimal etter alle mål. For n = 50 bruker naiv rekursjon sekunder, mens iterasjon bruker mikrosekunder.

import time

def fib_rec(n):
    if n <= 1: return n
    return fib_rec(n-1) + fib_rec(n-2)   # O(2^n)

def fib_iter(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a                              # O(n) time, O(1) space

# Timing comparison for n=35
start = time.time()
fib_rec(35)
print(f'Recursive n=35: {time.time()-start:.3f}s')

start = time.time()
fib_iter(35)
print(f'Iterative n=35: {time.time()-start:.6f}s')

print(fib_iter(100))  # handles large n

Tretraversering: rekursiv kontra iterativ

Rekursiv tretraversering er naturlig ryddig fordi trestrukturen speiler rekursjon. Men for et dypt skjevt tre (i praksis en lenket liste) er rekursjonsdybden lik trehøyden = O(n), noe som kan føre til stack overflow. Den iterative versjonen med en eksplisitt stakk har ingen dybdebegrensning og lar stakkstørrelsen vokse på heapen i stedet for i kallstakken.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val   = val; self.left = left; self.right = right

def preorder_rec(root, result=None):
    if result is None: result = []
    if root:
        result.append(root.val)
        preorder_rec(root.left, result)
        preorder_rec(root.right, result)
    return result

def preorder_iter(root):
    if not root: return []
    result, stack = [], [root]
    while stack:
        node = stack.pop()
        result.append(node.val)
        if node.right: stack.append(node.right)
        if node.left:  stack.append(node.left)
    return result

root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3))
print(preorder_rec(root))   # [1, 2, 4, 5, 3]
print(preorder_iter(root))  # [1, 2, 4, 5, 3]

Flettesortering: rekursiv kontra iterativ (bottom-up)

Flettesortering er naturlig rekursiv (del opp, kall rekursivt, flett). Den iterative bottom-up-flettesorteringen unngår rekursjon helt: start med delarrayer av størrelse 1, flett sammen tilstøtende par til delarrayer av størrelse 2, deretter størrelse 4 og så videre, ved å doble delarray-størrelsen i hver gjennomgang. Bottom-up-flettesortering bruker O(n) tid, O(n) plass (til flettingsbufferen) og O(1) stakkplass.

def merge_sort_iterative(arr):
    n = len(arr)
    size = 1
    while size < n:
        for start in range(0, n, 2 * size):
            mid   = min(start + size, n)
            end   = min(start + 2 * size, n)
            left  = arr[start:mid]
            right = arr[mid:end]
            # Merge
            i = j = 0
            for k in range(start, end):
                if i < len(left) and (j >= len(right) or left[i] <= right[j]):
                    arr[k] = left[i]; i += 1
                else:
                    arr[k] = right[j]; j += 1
        size *= 2
    return arr

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

Når rekursjon klart er bedre

Rekursjon er best når problemet har en trelignende struktur som kan avbildes direkte på kallgrafen, når grunntilfellene er naturlige, og når dybden er begrenset (O(log n) for balanserte trær og del-og-hersk). Eksempler er JSON-parsing, katalogtraversering, spilltrær og backtracking-problemer. I slike tilfeller er den rekursive koden kortere, tydeligere og enklere å bevise korrekt enn den tilsvarende iterative versjonen.

# Recursion is clearest for JSON-like nested structures
def flatten(nested):
    result = []
    for item in nested:
        if isinstance(item, list):
            result.extend(flatten(item))  # recurse on sub-list
        else:
            result.append(item)
    return result

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

Når iterasjon klart er bedre

Iterasjon er riktig valg når: dybden er O(n) og n er stor (mer enn ca. 500 i trygg Python-kode), de rekursive og iterative versjonene er like lesbare (Fibonacci, fakultet), eller problemet i bunn og grunn er sekvensielt uten en naturlig oppdeling i delproblemer. Enkle løkker som behandler arrayer fra venstre mot høyre – løpende summer, skyvevinduer og to pekere – bør alltid implementeres iterativt.

# Iterative is clearest for sequential array processing
def running_max(nums):
    result = []
    curr_max = float('-inf')
    for n in nums:
        curr_max = max(curr_max, n)
        result.append(curr_max)
    return result

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

# No natural recursion here — iteration is the only sensible choice

Konvertere rekursiv DFS til iterasjon

En systematisk framgangsmåte er at all rekursiv DFS kan gjøres iterativ ved å legge de rekursive argumentene på en eksplisitt stakk. Den viktige innsikten er at det rekursive kallet f(args) tilsvarer å legge args på stakken og kjøre en løkke. For post-order-behandling, der du trenger resultatene fra barna før forelderen, kan det være nødvendig med en totrinnsmetode eller et besøkt-flagg.

# Post-order iterative using two stacks
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val=val; self.left=left; self.right=right

def postorder_iter(root):
    if not root: return []
    s1, s2 = [root], []
    while s1:
        node = s1.pop()
        s2.append(node.val)
        if node.left:  s1.append(node.left)
        if node.right: s1.append(node.right)
    return s2[::-1]  # reverse gives post-order

root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3))
print(postorder_iter(root))  # [4, 5, 2, 3, 1]

Ytelsesoverhead ved rekursjon

Hvert rekursive kall i Python har merkbar overhead: et nytt stack frame opprettes (minne allokeres på heapen), lokale variabler initialiseres, og en peker til returadressen lagres. Ytelsesmålinger viser at overheaden for funksjonskall i Python er omtrent 100–200 nanosekunder per kall. Ved en rekursjonsdybde på 10^6 blir dette 0,1–0,2 sekunder med ren overhead, uavhengig av arbeidet algoritmen utfører. Iterative løkker unngår denne overheaden fullstendig.

import time

def rec_sum(n):
    if n == 0: return 0
    return n + rec_sum(n - 1)

def iter_sum(n):
    total = 0
    for i in range(n + 1):
        total += i
    return total

import sys; sys.setrecursionlimit(10000)

n = 5000
start = time.time()
for _ in range(100): rec_sum(n)
print(f'Recursive sum({n}) x100: {(time.time()-start)*1000:.2f}ms')

start = time.time()
for _ in range(100): iter_sum(n)
print(f'Iterative sum({n}) x100: {(time.time()-start)*1000:.2f}ms')

Ta avgjørelsen i et intervju

I et kodeintervju bør du spørre dersom du kan velge: «Er rekursjonsdybden begrenset til O(log n)?» Hvis ja, er rekursjon greit. «Er rekursjonsdybden O(n)?» – foretrekk iterasjon, eller nevn at du ville konvertert til iterativ kode i produksjon. «Er problemet naturlig treformet eller et del-og-hersk-problem?» – foretrekk rekursjon. «Er problemet en sekvensiell gjennomgang?» – bruk iterasjon.

Forklar alltid begrunnelsen din: «Jeg bruker rekursjon her fordi dybden er O(log n) for et balansert BST, så O(log n) stakkplass er akseptabelt.»

Oppsummering: avveiningstabell

For å oppsummere avveiningene: Rekursiv kode er ofte kortere og speiler problemstrukturen, men bruker O(depth) stakkplass og har overhead fra funksjonskall. Iterativ kode er lengre, men bruker O(1) stakkplass og unngår rekursjonsbegrensninger. Memoisert rekursjon (neste leksjon) er et mellomnivå: Du beholder rekursjonens tydelighet samtidig som du eliminerer unødvendig ny beregning. Oppgi alltid plasskompleksiteten tydelig, inkludert kallstakkplass, når du analyserer løsningen din.

rows = [
    ('Factorial',   'O(n) / O(1)', 'O(n) / O(1)', 'Same time; iter wins on space'),
    ('Fibonacci',   'O(2^n) / O(n)', 'O(n) / O(1)', 'Iter massively wins'),
    ('Binary search','O(log n) / O(log n)', 'O(log n) / O(1)', 'Iter wins on space'),
    ('Tree DFS',    'O(n) / O(h)',  'O(n) / O(h)', 'Equal; rec cleaner'),
    ('Merge sort',  'O(n log n) / O(log n)', 'O(n log n) / O(1)', 'BU-iter wins on stack'),
]
for name, rec, it, note in rows:
    print(f'{name:<15} rec={rec:<22} iter={it:<22} {note}')

Hurtigsjekk

Test forståelsen din av konseptene fra Data Structures & Algorithms — Coding Interview Prep i denne leksjonen.

Leksjonsoppsummering

I denne leksjonen lærte du: rekursjon foretrekkes når dybden er O(log n) eller problemet naturlig er treformet; iterasjon når dybden er O(n) eller problemet er sekvensielt, naiv rekursiv Fibonacci har tidskompleksiteten O(2^n) – den iterative versjonen bruker O(n) tid og O(1) plass, og all rekursiv DFS kan konverteres til iterativ ved å håndtere en eksplisitt stakk på heapen. Neste trinn er å bruke memoisering for å eliminere unødvendige rekursive kall.

Gratis å komme i gang

Lær deg Python med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Avveininger mellom rekursiv og iterativ løsning» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien DSA Interview Prep, inkludert «Avveininger mellom rekursiv og iterativ løsning», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i DSA Interview Prep inneholder totalt 4 leksjoner.

Hva lærer jeg i «Avveininger mellom rekursiv og iterativ løsning»?

Gjør rekursivt fakultet og Fibonacci om til iterative løkker, og forklar når Pythons rekursjonsgrense og stakkstørrelse gjør iterasjon til det beste valget. Du øver på DSA Interview Prep med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med DSA Interview Prep?

Ingen tidligere erfaring er nødvendig. DSA Interview Prep på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Avveininger mellom rekursiv og iterativ løsning»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne DSA Interview Prep-leksjonen?

Ja. Alle DSA Interview Prep-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Rekursjonsrammeverk: basistilfelle, tillit, bygg
  2. Visualisering av kallstakken
  3. Avveininger mellom rekursiv og iterativ løsning
  4. Memoisation: hurtigbufring av rekursive resultater
← Tilbake til DSA Interview Prep