Förberedelse inför kodningsintervjuer · Lektion

Rekursionsramverk: basfall, tillit, bygg

Tillämpa trestegsmetoden för att skriva korrekta rekursiva lösningar för fakultet, potens och siffersumma utan att följa varje anrop.

Lektion 1 av 413 steg

Rekursionsramverk: basfall, tillit, bygg ä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 rekursion känns svår

De flesta nybörjare försöker följa varje rekursivt anrop i huvudet, vilket snabbt blir överväldigande även vid rekursion på fem nivåer. Det professionella tillvägagångssättet är att använda ett trestegsramverk — basfall, tillit, uppbyggnad — som låter er skriva korrekta rekursiva funktioner utan att mentalt simulera hela anropsträdet.

Ramverket kallas ibland för leap of faith: ni litar på att funktionen fungerar för mindre indata och använder det antagandet för att bygga lösningen för större indata.

Steg 1: Definiera basfallet

Det basfallet är den enklaste indata för vilken svaret kan bestämmas utan ytterligare rekursion. Varje rekursiv funktion måste ha minst ett basfall; utan det fortsätter funktionen att anropa sig själv för evigt (stack overflow). Bra basfall är: en tom lista, ett enda element, n == 0, n == 1 eller att problemet reduceras till en trivial identitet.

Skriv basfallet först, innan någon rekursiv logik. Identifiera det genom att fråga: 'Vilken är den minsta versionen av det här problemet som jag kan besvara direkt?'

# Base cases for common problems
def factorial(n):
    if n == 0:          # base case: 0! = 1
        return 1
    # ... recursive step below

def sum_list(lst):
    if not lst:         # base case: sum of empty list is 0
        return 0
    # ...

def height(node):
    if node is None:    # base case: height of null node is 0
        return 0
    # ...

print('Base cases identified')

Steg 2: Lita på det rekursiva anropet

Tillitssteget är ett språng i tron: anta att funktionen redan fungerar korrekt för all indata som är strikt mindre än den aktuella. Ni behöver inte bevisa det för varje mindre indata just nu — det induktiva beviset garanterar det. Anropa helt enkelt funktionen med det mindre delproblemet och lita på att den returnerar rätt resultat.

Det här är steget som nybörjare hoppar över och i stället försöker simulera mentalt. Motstå den impulsen; metoden fungerar även vid godtyckligt djup rekursion när ni väl har gjort ramverket till ert eget.

# Trust example: sum_list([3, 1, 4, 1, 5])
# Trust: sum_list([1, 4, 1, 5]) = 11  (we TRUST this, don't trace it)
# Build: 3 + 11 = 14

# So:
def sum_list(lst):
    if not lst:
        return 0
    # Trust that sum_list(lst[1:]) returns sum of the rest
    return lst[0] + sum_list(lst[1:])

print(sum_list([3, 1, 4, 1, 5]))  # 14

Steg 3: Bygg lösningen

Byggsteget kombinerar resultatet från det betrodda delproblemet med det aktuella elementets bidrag för att skapa svaret för hela indatan. Det är vanligtvis en enda rad: tillämpa en operation på det aktuella elementet och resultatet från det rekursiva anropet. Vanliga sätt att bygga lösningen är att addera till en summa, lägga till först i en lista, öka en räknare eller kombinera två delresultat.

def factorial(n):
    if n == 0:
        return 1
    # Trust: factorial(n-1) gives (n-1)!
    # Build: n * (n-1)! = n!
    return n * factorial(n - 1)

def power(base, exp):
    if exp == 0:
        return 1
    # Trust: power(base, exp-1) gives base^(exp-1)
    # Build: base * base^(exp-1) = base^exp
    return base * power(base, exp - 1)

print(factorial(6))    # 720
print(power(2, 10))    # 1024

Tillämpa ramverket på siffersumman

Problem: beräkna siffersumman för ett icke-negativt heltal. Basfall: n == 0 → summan är 0 (eller n < 10 → n självt). Tillit: sumDigits(n // 10) returnerar summan av alla siffror utom den sista. Bygg: addera den sista siffran n % 10 till det betrodda resultatet. Ramverket ger lösningen i tre deklarativa steg.

def sumDigits(n):
    if n < 10:
        return n            # base case: single digit
    # Trust: sumDigits(n // 10) gives sum of all digits except last
    # Build: add the last digit
    return n % 10 + sumDigits(n // 10)

print(sumDigits(0))      # 0
print(sumDigits(7))      # 7
print(sumDigits(123))    # 6
print(sumDigits(9999))   # 36

Fibonacci: två delproblem

Fibonacci kräver två rekursiva anrop: fib(n-1) och fib(n-2). Tillämpa ramverket: basfallen är fib(0) = 0 och fib(1) = 1. Tillit: båda de mindre anropen returnerar rätt Fibonacci-värden. Bygg: returnera deras summa. Den här naiva implementationen har tidskomplexiteten O(2^n) — det åtgärdar vi i lektionen om memoisering.

def fib(n):
    if n <= 1:
        return n      # base cases: fib(0)=0, fib(1)=1
    # Trust both smaller sub-problems
    return fib(n - 1) + fib(n - 2)

for i in range(8):
    print(f'fib({i}) = {fib(i)}')  # 0,1,1,2,3,5,8,13

Vänd en sträng rekursivt

Problem: vänd en sträng rekursivt. Basfall: en tom sträng eller ett enda tecken — den är redan omvänd. Tillit: reverse(s[1:]) returnerar omvändningen av allt efter det första tecknet. Bygg: lägg till det första tecknet sist i den omvända resten. Ramverket ger en lösning på tre rader.

def reverse_str(s):
    if len(s) <= 1:
        return s            # base case
    # Trust: reverse_str(s[1:]) = reverse of 'ello' for 'hello'
    # Build: append first character at end
    return reverse_str(s[1:]) + s[0]

print(reverse_str(''))        # ''
print(reverse_str('a'))       # 'a'
print(reverse_str('hello'))   # 'olleh'
print(reverse_str('racecar')) # 'racecar'

Räkna förekomster rekursivt

Problem: räkna hur många gånger ett målvärde förekommer i en lista, rekursivt. Basfall: en tom lista — antalet är 0. Tillit: count(lst[1:], target) returnerar antalet i resten av listan. Bygg: addera 1 om det första elementet matchar målet, annars addera 0. Varje rekursivt steg närmar sig basfallet genom att minska listans storlek med 1.

def count_occurrences(lst, target):
    if not lst:
        return 0
    # Trust: count in rest of list is handled recursively
    # Build: add 1 if first element matches, else 0
    return (1 if lst[0] == target else 0) + count_occurrences(lst[1:], target)

print(count_occurrences([1, 2, 3, 2, 4, 2], 2))  # 3
print(count_occurrences([], 5))                    # 0
print(count_occurrences([7, 7, 7], 7))             # 3

Kontrollera om en lista är sorterad

Problem: kontrollera rekursivt om en lista är sorterad i stigande ordning. Basfall: en lista med 0 eller 1 element är alltid sorterad. Tillit: is_sorted(lst[1:]) anger om resten av listan är sorterad. Bygg: listan är sorterad om det första elementet är <= det andra OCH resten av listan är sorterad. Det här är ett tydligt exempel där byggsteget använder ett logiskt AND mellan två villkor.

def is_sorted(lst):
    if len(lst) <= 1:
        return True
    # Trust: is_sorted(lst[1:]) tells us if tail is sorted
    # Build: head <= second element AND tail is sorted
    return lst[0] <= lst[1] and is_sorted(lst[1:])

print(is_sorted([]))           # True
print(is_sorted([1]))          # True
print(is_sorted([1, 2, 3, 4])) # True
print(is_sorted([1, 3, 2, 4])) # False

Binärsökning rekursivt (igen)

Binärsökning uttryckt rekursivt med hjälp av ramverket: basfall: lo > hi → målet hittades inte (returnera -1). Tillit: det rekursiva anropet på rätt halva hittar målet eller returnerar -1. Bygg: beräkna mid, jämför och anropa rätt halva. Den rekursiva formen visar tydligt strukturen för dela och härska, även om den iterativa formen föredras i produktionskod eftersom den använder O(1) minne.

def binary_search(arr, target, lo, hi):
    if lo > hi:          # base case: search space exhausted
        return -1
    mid = lo + (hi - lo) // 2
    if arr[mid] == target:
        return mid
    # Trust both halves return correct results
    if arr[mid] < target:
        return binary_search(arr, target, mid + 1, hi)
    else:
        return binary_search(arr, target, lo, mid - 1)

arr = [1, 3, 5, 7, 9, 11]
print(binary_search(arr, 7, 0, len(arr) - 1))   # 3
print(binary_search(arr, 4, 0, len(arr) - 1))   # -1

När ska ni använda rekursion i stället för iteration

Rekursion passar utmärkt när problemet naturligt kan delas upp i mindre delproblem av samma typ (träd, dela och härska, backtracking). Iteration föredras när: rekursionsdjupet är stort (vilket medför risk för stack overflow i Python, där standardvärdet är ungefär 1000), de rekursiva och iterativa versionerna är lika tydliga eller problemet är en enkel loop (fakultet, Fibonacci utan memoisering).

En bra tumregel är: om det känns naturligt att rita ett rekursionsträd, använd rekursion. Om trädet är en rak linje (svansrekursion), konvertera till iteration.

import sys

# Python's default recursion limit
print('Recursion limit:', sys.getrecursionlimit())  # 1000

# A list of 2000 elements would overflow the recursive sum_list
# Use iteration for safety:
def sum_list_iter(lst):
    total = 0
    for x in lst:
        total += x
    return total

big = list(range(2000))
print(sum_list_iter(big))  # 1999000 — no stack overflow

Snabbtest

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

Lektionssammanfattning

I den här lektionen har ni lärt er: ramverket i tre steg är Basfall (det enklaste kända svaret), Tillit (anta att delproblemet är löst) och Bygg (kombinera det aktuella elementet med det betrodda resultatet), skriv basfallen först och undvik att mentalt följa hela anropsträd samt använd iteration när rekursionsdjupet riskerar att orsaka stack overflow eller när de rekursiva och iterativa formerna är lika tydliga. Härnäst visualiserar vi anropsstacken i detalj.

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 ”Rekursionsramverk: basfall, tillit, bygg” gratis?

Ja – hela texten till ”Rekursionsramverk: basfall, tillit, bygg” 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 ”Rekursionsramverk: basfall, tillit, bygg”?

Tillämpa trestegsmetoden för att skriva korrekta rekursiva lösningar för fakultet, potens och siffersumma utan att följa varje anrop. 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 ”Rekursionsramverk: basfall, tillit, bygg”?

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. Rekursionsramverk: basfall, tillit, bygg
  2. Visualisera anropsstacken
  3. Avvägningar mellan rekursiva och iterativa lösningar
  4. Memoisation: cacha rekursiva resultat
← Tillbaka till Förberedelse inför kodningsintervjuer