DSA Interview Prep · Les

Permutaties en combinaties

Som alle permutaties van een lijst op, zowel met als zonder dubbele elementen, en genereer alle k-combinaties en varianten van combinatiesommen.

Les 3 van 413 stappen

Permutaties en combinaties is een gratis DSA Interview Prep-les op CoddyKit. Dit is les 3 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject DSA Interview Prep. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus DSA Interview Prep bevat in totaal 4 lessen.

Permutaties versus combinaties

Permutaties zijn rangschikkingen waarbij de volgorde van belang is: [1,2,3] en [3,2,1] zijn verschillend. Het aantal permutaties van n items is n!. Combinaties zijn selecties waarbij de volgorde niet van belang is: {1,2} kiezen is hetzelfde als {2,1} kiezen. Het aantal k-combinaties uit n items is C(n,k) = n! / (k! × (n-k)!). Beide patronen zijn essentieel bij sollicitatieopgaven over tellen, opsommen en selecteren.

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)

Alle permutaties genereren

Gebruik een booleaanse array used om bij te houden welke elementen in het huidige pad zitten. Probeer bij elke stap elk ongebruikt element. Markeer het element na het verkennen opnieuw als ongebruikt. In tegenstelling tot deelverzamelingen is er geen start-index, omdat permutaties elementen in elke volgorde gebruiken. De recursie stopt wanneer 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]]

Permutaties op basis van verwisselen

Een alternatief is om het element op positie start te verwisselen met elk element van start tot n-1, daarna recursief verder te gaan en vervolgens de verwisseling terug te draaien. Hiermee wijzig je de array rechtstreeks, zonder een array used. Het belangrijkste inzicht is dat op elk niveau alles links van start vastligt en dat je kiest welk element je op positie start plaatst. Dit gebruikt iets minder geheugen en vormt de basis van Heap's algorithm.

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

Permutaties II: omgaan met duplicaten

Wanneer de invoer duplicaten bevat (bijvoorbeeld [1, 1, 2]), genereert de aanpak met de array used dubbele permutaties. Oplossing: sorteer de array en sla een duplicaat over als het vorige identieke element niet in deze recursieve aanroep is gebruikt. De voorwaarde is: if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continue. Zo dwing je af dat duplicaten altijd van links naar rechts worden gekozen.

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

Volgende permutatie (lexicografisch)

Volgende permutatie (LeetCode 31) zet een array ter plaatse om in de eerstvolgende lexicografisch grotere permutatie. Algoritme: (1) Zoek de meest rechtse index i waarvoor nums[i] < nums[i+1] geldt. (2) Zoek de meest rechtse index j waarvoor nums[j] > nums[i] geldt. (3) Verwissel nums[i] en nums[j]. (4) Keer het achterste deel na index i om. Als zo'n i niet bestaat, keer je de hele array om (je gaat dan terug naar de kleinste permutatie).

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]

k-combinaties met backtracking

Genereer alle combinaties van k elementen uit n elementen (LeetCode 77). Gebruik, net als bij deelverzamelingen, een startindex om te voorkomen dat je elementen opnieuw bezoekt en om de gesorteerde volgorde te behouden. Kap af wanneer er minder dan k - len(path) elementen over zijn: if len(nums) - i + 1 < k - len(path): break. Dit is gelijkwaardig aan de eerdere combine(n, k), maar werkt op een echte 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))  # 10

Combinatiesom: onbeperkt hergebruik

Combinatiesom (LeetCode 39) staat toe dat elk getal onbeperkt vaak wordt gebruikt. Het verschil met standaardcombinaties is dat je start niet naar i+1 verschuift, maar i doorgeeft (dezelfde index), zodat je het huidige element opnieuw kunt gebruiken. Afkappen: als de resterende doelwaarde 0 wordt, voeg je het pad toe aan de resultaten; als de waarde negatief wordt, stop je. Sorteren maakt vroegtijdig stoppen mogelijk zodra alle resterende kandidaten groter zijn dan de resterende doelwaarde.

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]]

Combinatiesom II: geen hergebruik, met duplicaten

Combinatiesom II (LeetCode 40) gebruikt elk getal hoogstens één keer, maar de invoer kan duplicaten bevatten. Je combineert twee technieken: verschuif start naar i+1 (geen hergebruik) en sla duplicaten op hetzelfde niveau over (if i > start and nums[i] == nums[i-1]: continue) nadat je hebt gesorteerd. Dit combineert het omgaan met duplicaten uit Deelverzamelingen II met de beperking dat hergebruik bij combinaties niet is toegestaan.

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]]

Lettercombinaties van een telefoonnummer

Lettercombinaties (LeetCode 17) koppelt elk cijfer aan letters op een telefoontoetsenbord en genereert alle mogelijke lettercombinaties voor een gegeven cijferreeks. Dit is een backtrackingprobleem waarbij je op elke positie één letter uit de koppeling van het cijfer kiest en recursief verdergaat. Voor een reeks met lengte n en gemiddeld k letters per cijfer is de tijdcomplexiteit 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']

Permutaties en combinaties vergelijken

De belangrijkste structurele verschillen zijn: Permutaties — geen startindex, gebruik een array used of verwisselingen om hergebruik te voorkomen, de boom heeft op elk niveau n keuzes en in totaal n! bladeren. Combinaties — gebruik een startindex om de volgorde af te dwingen, met C(n,k) bladeren. Combinatiesom — verschuif de startindex niet voor hergebruik en kap af op basis van de doelwaarde. Als je een nieuw probleem in een van deze drie vormen herkent, heb je meteen het juiste sjabloon.

# 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}')

Complexiteit en tips voor sollicitatiegesprekken

De tijdcomplexiteit voor het opsommen is: permutaties O(n × n!), combinaties O(k × C(n,k)), combinatiesom O(n^(T/min_val)). De ruimtecomplexiteit is O(n) voor de recursiediepte plus O(uitvoer) voor de resultaten. Belangrijke tips: (1) Verduidelijk altijd of de volgorde van belang is (permutatie of combinatie). (2) Bespreek hoe je duplicaten afhandelt voordat ernaar wordt gevraagd. (3) Noem de afkapvoorwaarde altijd expliciet. (4) Vermeld bij grote n dat de uitvoer zelf exponentieel is — het algoritme is optimaal voor deze taak.

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)'

Snelle controle

Test je begrip van de concepten Data Structures & Algorithms — Coding Interview Prep uit deze les.

Samenvatting van de les

In deze les heb je geleerd dat permutaties een array used gebruiken en geen startindex hebben, waarmee ze n! rangschikkingen genereren, dat combinaties een startindex gebruiken die opschuift om hergebruik te voorkomen, waarmee ze C(n,k) selecties genereren, en dat duplicaten in beide problemen worden afgehandeld door te sorteren en herhaalde waarden op hetzelfde recursieniveau over te slaan. Hierna passen we backtracking toe op het N-damesprobleem en onderzoeken we het doorgeven van beperkingen.

Gratis beginnen

Leer Python met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
30
Lessen
120

Veelgestelde vragen

Is de les “Permutaties en combinaties” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad DSA Interview Prep, waaronder “Permutaties en combinaties”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus DSA Interview Prep bevat in totaal 4 lessen.

Wat leer ik in “Permutaties en combinaties”?

Som alle permutaties van een lijst op, zowel met als zonder dubbele elementen, en genereer alle k-combinaties en varianten van combinatiesommen. Je oefent met DSA Interview Prep door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met DSA Interview Prep te beginnen?

Ervaring vooraf is niet nodig. DSA Interview Prep op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.

Hoe lang duurt de les “Permutaties en combinaties”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over DSA Interview Prep?

Ja. Elke les over DSA Interview Prep bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Backtracking-sjabloon: kiezen, verkennen, keuze ongedaan maken
  2. Deelverzamelingen en machtsverzameling
  3. Permutaties en combinaties
  4. N-koninginnen en constraintpropagatie
← Terug naar DSA Interview Prep