Permutationer og kombinationer
Enumerér alle permutationer af en liste med og uden dublerede elementer, og generér alle k-kombinationer samt varianter af kombinationssum.
Permutationer og kombinationer er en gratis DSA Interview Prep-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i DSA Interview Prep, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. DSA Interview Prep-kurset indeholder 4 lektioner i alt.
Permutationer kontra kombinationer
Permutationer er ordninger, hvor rækkefølgen betyder noget: [1,2,3] og [3,2,1] er forskellige. Antallet af permutationer for n elementer er n!. Kombinationer er udvalg, hvor rækkefølgen er ligegyldig: At vælge {1,2} er det samme som {2,1}. Antallet af k-kombinationer fra n elementer er C(n,k) = n! / (k! × (n-k)!). Begge er centrale mønstre i interviewopgaver om optælling, opregning og udvælgelse.
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)Generering af alle permutationer
Brug et boolsk used-array til at holde styr på, hvilke elementer der er med i den aktuelle sti. Prøv hvert ubrugt element på hvert trin. Når udforskningen er færdig, markeres elementet som ubrugt igen. I modsætning til delmængder er der ikke noget start-indeks, fordi permutationer bruger elementerne i vilkårlig rækkefølge. Rekursionen afsluttes, 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 baseret på bytning
Et alternativ er at bytte elementet på position start med hvert element fra start til n-1, rekursere og derefter bytte tilbage. Det ændrer arrayet direkte uden et used-array. Den centrale pointe er, at alt til venstre for start er fast på hvert niveau, og at vi vælger, hvilket element der skal placeres på position start. Det bruger en smule mindre hukommelse og danner grundlag for Heaps algoritme.
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 orderPermutationer II: Håndtering af dubletter
Når inddataene indeholder dubletter (f.eks. [1, 1, 2]), genererer tilgangen med used-arrayet dublette permutationer. Løsning: Sortér arrayet, og spring en dublet over, hvis det forrige identiske element ikke blev brugt i dette rekursive kald. Betingelsen er: if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continue. Det sikrer, at dubletter altid vælges fra venstre mod højre.
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 6Næste permutation (leksikografisk)
Næste permutation (LeetCode 31) omdanner et array til dets næste leksikografisk større permutation direkte. Algoritme: (1) Find det højre-meste indeks i, hvor nums[i] < nums[i+1]. (2) Find det højre-meste indeks j, hvor nums[j] > nums[i]. (3) Byt nums[i] og nums[j]. (4) Vend suffikset efter indeks i. Hvis et sådant i ikke findes, vendes hele arrayet, så du går tilbage til den mindste permutation.
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 for k-kombinationer
Generér alle kombinationer af k elementer ud af n (LeetCode 77). Brug et startindeks som ved delmængder for at undgå at besøge elementer igen og bevare sorteret rækkefølge. Beskær, når der er færre end k - len(path) elementer tilbage: if len(nums) - i + 1 < k - len(path): break. Det svarer til den tidligere combine(n, k), men arbejder på et faktisk array.
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)) # 10Kombinationssum: Ubegrænset genbrug
Kombinationssum (LeetCode 39) tillader, at hvert tal bruges et ubegrænset antal gange. Forskellen fra standardkombinationer er, at du i stedet for at flytte start til i+1 sender i videre (samme indeks), så det aktuelle element kan genbruges. Beskæring: Hvis det resterende mål bliver 0, gemmes stien; hvis det bliver negativt, stoppes der. Sortering gør det muligt at afslutte tidligt, når alle resterende kandidater er større end det resterende mål.
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]]Kombinationssum II: Intet genbrug, med dubletter
Kombinationssum II (LeetCode 40) bruger hvert tal højst én gang, men inddataene kan indeholde dubletter. Her kombineres to teknikker: Flyt start til i+1 (intet genbrug), og spring dubletter over på samme niveau (if i > start and nums[i] == nums[i-1]: continue) efter sortering. Det samler håndteringen af dubletter fra delmængder II med begrænsningen om intet genbrug fra kombinationer.
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]]Bogstavkombinationer for et telefonnummer
Bogstavkombinationer (LeetCode 17) knytter hvert ciffer til bogstaverne på et telefontastatur og genererer alle mulige bogstavkombinationer for en given cifferstreng. Det er en backtracking-opgave, hvor du på hver position vælger ét bogstav fra cifrets tilknytning og rekursivt fortsætter. For en streng med længden n og gennemsnitligt k bogstaver pr. ciffer er tidskompleksiteten 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']Sammenligning af permutationer og kombinationer
De vigtigste strukturelle forskelle er: Permutationer — intet startindeks, brug et used-array eller byt for at undgå genbrug, træet har n valgmuligheder på hvert niveau og i alt n! blade. Kombinationer — brug et startindeks for at håndhæve rækkefølgen, med C(n,k) blade. Kombinationssum — flyt ikke startindekset frem, når elementer må genbruges, og beskær ud fra målet. Hvis du indplacerer en ny opgave i en af disse tre former, får du straks den rigtige skabelon.
# 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}')Kompleksitet og tips til jobsamtaler
Tidskompleksiteten ved opregning er: permutationer O(n × n!), kombinationer O(k × C(n,k)), kombinationssum O(n^(T/min_val)). Pladsforbruget er O(n) for rekursionsdybden plus O(resultat) for resultaterne. Vigtige tips: (1) Afklar altid, om rækkefølgen betyder noget (permutation eller kombination). (2) Nævn håndtering af dubletter, før du bliver spurgt. (3) Angiv altid beskæringsbetingelsen tydeligt. (4) Bemærk ved store n, at selve resultatet er eksponentielt — algoritmen er optimal til opgaven.
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)'Hurtig kontrol
Kontrollér din forståelse af begreberne fra Data Structures & Algorithms — Coding Interview Prep i denne lektion.
Opsummering af lektionen
I denne lektion lærte du: permutationer bruger et used-array og intet startindeks og genererer n! ordninger, kombinationer bruger et startindeks, der flyttes frem for at undgå genbrug, og genererer C(n,k) udvalg, og dubletter i begge opgaver håndteres ved at sortere og springe gentagne værdier over på samme rekursionsniveau. Næste emne er anvendelse af backtracking på problemet med N-dronninger og udforskning af begrænsningspropagering.
Lær Python 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
- 30
- Lektioner
- 120
Ofte stillede spørgsmål
Er lektionen “Permutationer og kombinationer” gratis?
Ja — alle 3 lektioner i læringssporet DSA Interview Prep, inklusive “Permutationer og kombinationer”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. DSA Interview Prep-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Permutationer og kombinationer”?
Enumerér alle permutationer af en liste med og uden dublerede elementer, og generér alle k-kombinationer samt varianter af kombinationssum. Du øver dig i DSA Interview Prep 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å DSA Interview Prep?
Der kræves ingen tidligere erfaring. DSA Interview Prep 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 3 af 4.
Hvor lang tid tager lektionen “Permutationer og kombinationer”?
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 DSA Interview Prep-lektion?
Ja. Alle DSA Interview Prep-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
- Backtracking-skabelon: vælg, udforsk, fortryd
- Delmængder og potensmængden
- Permutationer og kombinationer
- N-dronninger og begrænsningsformidling