Förberedelse inför kodningsintervjuer · Lektion

Permutationer och kombinationer

Enumerera alla permutationer av en lista, både med och utan dubblettelement, och generera alla k-kombinationer samt varianter av kombinationssummor.

Lektion 3 av 413 steg

Permutationer och kombinationer är en gratis lektion i Förberedelse inför kodningsintervjuer på CoddyKit. Detta är lektion 3 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.

Permutationer kontra kombinationer

Permutationer är arrangemang där ordningen spelar roll: [1,2,3] och [3,2,1] är olika. Antalet permutationer av n element är n!. Kombinationer är urval där ordningen inte spelar någon roll: att välja {1,2} är samma sak som att välja {2,1}. Antalet k-kombinationer från n element är C(n,k) = n! / (k! × (n-k)!). Båda är viktiga mönster i intervjuproblem om att räkna, räkna upp och välja.

import math

# Permutations
n = 4
print(f'Permutations of {n} items: {math.factorial(n)}')
# 4! = 24

# Combinations
for k in range(n+1):
    print(f'C({n},{k}) = {math.comb(n,k)}')
# C(4,0)=1, C(4,1)=4, C(4,2)=6, C(4,3)=4, C(4,4)=1
# Sum = 2^4 = 16 (total subsets)

Generera alla permutationer

Använd en boolesk array used för att hålla reda på vilka element som ingår i den aktuella vägen. Försök vid varje steg med alla oanvända element. När utforskningen är klar markerar ni elementet som oanvänt igen. Till skillnad från delmängder finns inget start-index, eftersom permutationer använder elementen i valfri ordning. Rekursionen avslutas när len(path) == n.

def permutations(nums):
    result = []
    used = [False] * len(nums)
    def backtrack(path):
        if len(path) == len(nums):
            result.append(list(path))
            return
        for i, num in enumerate(nums):
            if not used[i]:
                used[i] = True         # CHOOSE
                path.append(num)
                backtrack(path)        # EXPLORE
                path.pop()             # UNCHOOSE
                used[i] = False
    backtrack([])
    return result

print(permutations([1, 2, 3]))
# [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

Permutationer med byte

Ett alternativ är att byta elementet på position start med varje element från start till n-1, göra ett rekursivt anrop och sedan byta tillbaka. Detta ändrar arrayen på plats utan en used-array. Kärninsikten är att allt till vänster om start är fixerat på varje nivå, och att vi väljer vilket element som ska placeras på position start. Metoden använder något mindre minne och ligger till grund för Heap-algoritmen.

def permutations_swap(nums):
    result = []
    def backtrack(start):
        if start == len(nums):
            result.append(list(nums))
            return
        for i in range(start, len(nums)):
            nums[start], nums[i] = nums[i], nums[start]  # CHOOSE (swap)
            backtrack(start + 1)                          # EXPLORE
            nums[start], nums[i] = nums[i], nums[start]  # UNCHOOSE (swap back)
    backtrack(0)
    return result

print(permutations_swap([1, 2, 3]))
# Same 6 permutations, different order

Permutationer II: Hantera dubbletter

När indata innehåller dubbletter, till exempel [1, 1, 2], genererar metoden med en used-array dubbletter av permutationerna. Åtgärd: sortera arrayen och hoppa över en dubblett om det föregående identiska elementet inte har använts i detta rekursiva anrop. Villkoret är: if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continue. Detta tvingar fram att dubbletter alltid väljs från vänster till höger.

def permutations_unique(nums):
    nums.sort()
    result = []
    used = [False] * len(nums)
    def backtrack(path):
        if len(path) == len(nums):
            result.append(list(path))
            return
        for i in range(len(nums)):
            if used[i]: continue
            # Skip if this num is a duplicate and the previous dup was not used
            if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
                continue
            used[i] = True
            path.append(nums[i])
            backtrack(path)
            path.pop()
            used[i] = False
    backtrack([])
    return result

print(permutations_unique([1, 1, 2]))
# [[1,1,2],[1,2,1],[2,1,1]] — 3, not 6

Nästa permutation (lexikografisk)

Next Permutation (LeetCode 31) omvandlar en array till dess nästa lexikografiskt större permutation på plats. Algoritmen är: (1) Hitta det index i längst till höger där nums[i] < nums[i+1]. (2) Hitta det index j längst till höger där nums[j] > nums[i]. (3) Byt plats på nums[i] och nums[j]. (4) Vänd på suffixet efter index i. Om inget sådant i finns vänder ni på hela arrayen, vilket går runt till den minsta permutationen.

def next_permutation(nums):
    n = len(nums)
    # Step 1: find rightmost i where nums[i] < nums[i+1]
    i = n - 2
    while i >= 0 and nums[i] >= nums[i+1]:
        i -= 1
    if i >= 0:
        # Step 2: find rightmost j where nums[j] > nums[i]
        j = n - 1
        while nums[j] <= nums[i]:
            j -= 1
        # Step 3: swap
        nums[i], nums[j] = nums[j], nums[i]
    # Step 4: reverse suffix after i
    nums[i+1:] = nums[i+1:][::-1]
    return nums

print(next_permutation([1, 2, 3]))  # [1,3,2]
print(next_permutation([3, 2, 1]))  # [1,2,3] (wraps)
print(next_permutation([1, 1, 5]))  # [1,5,1]

Backtracking för k-kombinationer

Generera alla kombinationer av k element från n (LeetCode 77). Använd ett startindex, som för delmängder, för att undvika att besöka element igen och bevara sorterad ordning. Beskär när färre än k - len(path) element återstår: if len(nums) - i + 1 < k - len(path): break. Detta motsvarar den tidigare combine(n, k), men med en faktisk array som indata.

def combinations(nums, k):
    result = []
    def backtrack(start, path):
        if len(path) == k:
            result.append(list(path))
            return
        for i in range(start, len(nums)):
            # Pruning: not enough elements left
            if len(nums) - i < k - len(path):
                break
            path.append(nums[i])
            backtrack(i + 1, path)
            path.pop()
    backtrack(0, [])
    return result

print(combinations([1,2,3,4,5], 3))
# 10 combinations: C(5,3)
import math
print(math.comb(5,3))  # 10

Combination Sum: obegränsad återanvändning

Combination Sum (LeetCode 39) tillåter att varje tal används ett obegränsat antal gånger. Skillnaden mot vanliga kombinationer är att ni, i stället för att flytta start till i+1, skickar vidare i (samma index) för att kunna återanvända det aktuella elementet. Beskärning: om det återstående målvärdet blir 0 sparar ni den aktuella vägen; om det blir negativt avbryter ni. Sortering gör det möjligt att avsluta tidigt när alla återstående kandidater är större än det återstående målvärdet.

def combination_sum(candidates, target):
    candidates.sort()
    result = []
    def backtrack(start, path, remaining):
        if remaining == 0:
            result.append(list(path))
            return
        for i in range(start, len(candidates)):
            c = candidates[i]
            if c > remaining: break  # all remaining are too big
            path.append(c)
            backtrack(i, path, remaining - c)  # reuse allowed: pass i, not i+1
            path.pop()
    backtrack(0, [], target)
    return result

print(combination_sum([2, 3, 6, 7], 7))
# [[2,2,3],[7]]

Combination Sum II: ingen återanvändning, med dubbletter

Combination Sum II (LeetCode 40) använder varje tal högst en gång, men indata kan innehålla dubbletter. Metoden kombinerar två tekniker: flytta start till i+1 (ingen återanvändning) och hoppa över dubbletter på samma nivå (if i > start and nums[i] == nums[i-1]: continue) efter sortering. Detta förenar dubbletthanteringen från Subsets II med begränsningen att element inte får återanvändas från Combinations.

def combination_sum_ii(candidates, target):
    candidates.sort()
    result = []
    def backtrack(start, path, remaining):
        if remaining == 0:
            result.append(list(path))
            return
        for i in range(start, len(candidates)):
            if candidates[i] > remaining: break
            # Skip duplicates at same level
            if i > start and candidates[i] == candidates[i-1]:
                continue
            path.append(candidates[i])
            backtrack(i + 1, path, remaining - candidates[i])  # no reuse: i+1
            path.pop()
    backtrack(0, [], target)
    return result

print(combination_sum_ii([10,1,2,7,6,1,5], 8))
# [[1,1,6],[1,2,5],[1,7],[2,6]]

Bokstavskombinationer för telefonnummer

Letter Combinations (LeetCode 17) kopplar varje siffra till bokstäverna på en telefonknappsats och genererar alla möjliga bokstavskombinationer för en given siffersträng. Detta är ett backtrackingproblem där vi vid varje position väljer en bokstav från den mappning som hör till siffran och går rekursivt vidare. För en sträng med längden n och i genomsnitt k bokstäver per siffra är tidskomplexiteten O(kⁿ).

def letter_combinations(digits):
    if not digits: return []
    phone = {
        '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
        '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
    }
    result = []
    def backtrack(index, path):
        if index == len(digits):
            result.append(''.join(path))
            return
        for letter in phone[digits[index]]:
            path.append(letter)
            backtrack(index + 1, path)
            path.pop()
    backtrack(0, [])
    return result

print(letter_combinations('23'))
# ['ad','ae','af','bd','be','bf','cd','ce','cf']

Jämförelse mellan permutationer och kombinationer

De viktigaste strukturella skillnaderna är: Permutationer — inget startindex, använd en used-array eller byte för att undvika återanvändning, trädet har n val på varje nivå och totalt n! löv. Kombinationer — använd ett startindex för att upprätthålla ordningen, vilket ger C(n,k) löv. Combination Sum — flytta inte fram startindexet när element får återanvändas och beskär utifrån målvärdet. Om ni kopplar ett nytt problem till en av dessa tre former får ni rätt mall direkt.

# Pattern summary:
# Permutations: for i in range(n); if not used[i]; no start advancement
# Combinations: for i in range(start, n); advance start → i+1
# Combo Sum (reuse): for i in range(start, n); advance start → i (same)

# Quick reference:
import math
n = 5
print(f'Perm({n})   = n! = {math.factorial(n)}')
print(f'Comb({n},2) = C(n,k) = {math.comb(n,2)}')
print(f'Comb({n},3) = {math.comb(n,3)}')
# Also: subsets = sum(C(n,k) for k=0..n) = 2^n
print(f'Subsets({n}) = 2^n = {2**n}')

Komplexitet och intervjutips

Tidskomplexiteten för uppräkning är: Permutationer O(n × n!), Kombinationer O(k × C(n,k)), Combination Sum O(n^(T/min_val)). Utrymmeskomplexiteten är O(n) för rekursionsdjupet plus O(output) för resultaten. Viktiga tips: (1) Klargör alltid om ordningen spelar roll (permutation eller kombination). (2) Nämn hanteringen av dubbletter innan ni blir tillfrågade. (3) Ange alltid beskärningsvillkoret uttryckligen. (4) För stora n bör ni påpeka att själva resultatet är exponentiellt — algoritmen är optimal för uppgiften.

import math

# Complexity for n=10
n = 10
print(f'Permutations(10): {math.factorial(n):,} results')
print(f'Combinations(10,5): {math.comb(n,5):,} results')
print(f'Subsets(10): {2**n:,} results')

# For interview: state which pattern
# 'This is a combinations problem because order doesnt matter'
# 'I will use a start index to avoid revisiting elements'
# 'Pruning: when sum exceeds target, break (after sorting)'

Snabbtest

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

Sammanfattning av lektionen

I den här lektionen lärde ni er: permutationer använder en used-array och inget startindex, och genererar n! arrangemang, kombinationer använder ett startindex som flyttas fram för att undvika återanvändning, och genererar C(n,k)-urval, och dubbletter i båda problemen hanteras genom att sortera och hoppa över upprepade värden på samma rekursionsnivå. Härnäst tillämpar vi backtracking på N-Queens-problemet och utforskar propagering av begränsningar.

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 ”Permutationer och kombinationer” gratis?

Ja – hela texten till ”Permutationer och kombinationer” 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 ”Permutationer och kombinationer”?

Enumerera alla permutationer av en lista, både med och utan dubblettelement, och generera alla k-kombinationer samt varianter av kombinationssummor. 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 3 av 4.

Hur lång tid tar lektionen ”Permutationer och kombinationer”?

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. Backtracking-mall: välj, utforska, välj bort
  2. Delmängder och potensmängd
  3. Permutationer och kombinationer
  4. N-drottningar och constraint propagation
← Tillbaka till Förberedelse inför kodningsintervjuer