DSA Interview Prep · Pelajaran

Permutasi dan Kombinasi

Senaraikan semua permutasi bagi suatu senarai dengan dan tanpa unsur pendua, serta jana semua kombinasi-k dan variasi jumlah kombinasi.

Pelajaran 3 daripada 413 langkah

Permutasi dan Kombinasi ialah pelajaran DSA Interview Prep percuma di CoddyKit. Ini ialah pelajaran 3 daripada 4. Sebanyak 3 pelajaran dalam laluan pembelajaran ini boleh dibaca sepenuhnya secara percuma — selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan praktikal dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran DSA Interview Prep, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus DSA Interview Prep merangkumi sejumlah 4 pelajaran.

Permutasi berbanding Kombinasi

Permutasi ialah susunan yang mementingkan tertib: [1,2,3] dan [3,2,1] adalah berbeza. Bilangan permutasi bagi n item ialah n!. Kombinasi ialah pemilihan yang tidak mementingkan tertib: memilih {1,2} adalah sama dengan {2,1}. Bilangan kombinasi-k daripada n item ialah C(n,k) = n! / (k! × (n-k)!). Kedua-duanya ialah pola penting dalam masalah temu duga yang melibatkan pengiraan, penyenaraian dan pemilihan.

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)

Menjana Semua Permutasi

Gunakan tatasusunan nilai benar/palsu used untuk menjejak elemen yang berada dalam laluan semasa. Pada setiap langkah, cuba setiap elemen yang belum digunakan. Selepas meneroka, tandakan elemen itu sebagai belum digunakan semula. Berbeza daripada subhimpunan, tiada indeks start kerana permutasi menggunakan elemen dalam sebarang tertib. Rekursi tamat apabila 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]]

Permutasi Berasaskan Pertukaran

Pendekatan alternatif: tukar elemen pada kedudukan start dengan setiap elemen dari start hingga n-1, lakukan rekursi, kemudian tukar semula. Pendekatan ini mengubah tatasusunan di tempat asal tanpa tatasusunan used. Wawasan utamanya ialah pada setiap aras, segala-galanya di sebelah kiri start sudah ditetapkan, dan kita memilih elemen yang hendak diletakkan pada kedudukan start. Pendekatan ini menggunakan memori dengan lebih cekap dan menjadi asas kepada algoritma Heap.

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

Permutasi II: Mengendalikan Pendua

Apabila masukan mempunyai pendua (contohnya, [1, 1, 2]), pendekatan tatasusunan used akan menjana permutasi pendua. Penyelesaiannya: isih tatasusunan, kemudian langkau pendua jika elemen serupa sebelumnya belum digunakan dalam panggilan rekursif ini. Syaratnya: if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continue. Hal ini memastikan pendua sentiasa dipilih dari kiri ke kanan.

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

Permutasi Seterusnya (Mengikut Tertib Leksikografi)

Permutasi Seterusnya (LeetCode 31) mengubah tatasusunan kepada permutasi seterusnya yang lebih besar mengikut tertib leksikografi di tempat asal. Algoritmanya: (1) Cari indeks paling kanan i yang memenuhi nums[i] < nums[i+1]. (2) Cari indeks paling kanan j yang memenuhi nums[j] > nums[i]. (3) Tukar nums[i] dengan nums[j]. (4) Terbalikkan akhiran selepas indeks i. Jika indeks i sedemikian tidak wujud, terbalikkan seluruh tatasusunan (kembali kepada permutasi terkecil).

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]

Penjejakan Balik Kombinasi-k

Jana semua kombinasi k elemen daripada n (LeetCode 77). Gunakan indeks mula (seperti dalam subhimpunan) untuk mengelakkan penggunaan semula elemen dan mengekalkan tertib tersusun. Pangkas apabila kurang daripada k - len(path) elemen yang berbaki: if len(nums) - i + 1 < k - len(path): break. Ini bersamaan dengan combine(n, k) yang dinyatakan sebelum ini, tetapi beroperasi pada tatasusunan sebenar.

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

Jumlah Kombinasi: Guna Semula Tanpa Had

Jumlah Kombinasi (LeetCode 39) membenarkan setiap nombor digunakan tanpa had. Perbezaannya daripada kombinasi biasa: bukannya memajukan start kepada i+1, hantarkan i (indeks yang sama) untuk membolehkan penggunaan semula elemen semasa. Pemangkasan: jika sasaran yang berbaki menjadi 0, rekodkan laluan; jika nilainya menjadi negatif, hentikan carian. Pengisihan membolehkan penamatan awal apabila semua calon yang berbaki melebihi sasaran yang berbaki.

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

Jumlah Kombinasi II: Tanpa Guna Semula, Dengan Pendua

Jumlah Kombinasi II (LeetCode 40) menggunakan setiap nombor paling banyak sekali, tetapi masukan mungkin mengandungi pendua. Gabungan dua teknik digunakan: majukan start kepada i+1 (tanpa guna semula), dan langkau pendua pada aras yang sama (if i > start and nums[i] == nums[i-1]: continue) selepas pengisihan. Ini menggabungkan pengendalian pendua daripada Subhimpunan II dengan kekangan tanpa guna semula daripada kombinasi.

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

Kombinasi Huruf daripada Nombor Telefon

Kombinasi Huruf (LeetCode 17) memetakan setiap digit kepada huruf pada pad kekunci telefon dan menjana semua kombinasi huruf yang mungkin untuk rentetan digit yang diberikan. Ini ialah masalah penjejakan balik: pada setiap kedudukan, kita memilih satu huruf daripada pemetaan digit tersebut lalu melakukan rekursi. Bagi rentetan sepanjang n dengan purata k huruf bagi setiap digit, kerumitan masa ialah 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']

Membandingkan Permutasi dan Kombinasi

Perbezaan struktur utama: Permutasi — tiada indeks mula, gunakan tatasusunan used atau pertukaran untuk mengelakkan guna semula, pepohon mempunyai n pilihan pada setiap aras dan sejumlah n! daun. Kombinasi — gunakan indeks mula untuk menguatkuasakan tertib, dengan C(n,k) daun. Jumlah Kombinasi — tiada pemajuan indeks mula untuk membolehkan guna semula, dan pangkas berdasarkan sasaran. Memadankan mana-mana masalah baharu dengan salah satu daripada tiga bentuk ini akan memberikan templat yang betul dengan serta-merta.

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

Kerumitan dan Petua Temu Duga

Kerumitan masa untuk penyenaraian: Permutasi O(n × n!), Kombinasi O(k × C(n,k)), Jumlah Kombinasi O(n^(T/min_val)). Ruang ialah O(n) untuk kedalaman rekursi ditambah O(output) untuk hasil. Petua utama: (1) Sentiasa jelaskan sama ada tertib penting (permutasi atau kombinasi). (2) Nyatakan pengendalian pendua sebelum ditanya. (3) Sentiasa nyatakan syarat pemangkasan dengan jelas. (4) Bagi n yang besar, nyatakan bahawa hasil itu sendiri adalah eksponen — algoritma ini optimum untuk tugas tersebut.

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

Semakan Pantas

Uji pemahaman anda tentang konsep Struktur Data & Algoritma — Persediaan Temu Duga Pengekodan daripada pelajaran ini.

Imbas Kembali Pelajaran

Dalam pelajaran ini, anda telah belajar bahawa: permutasi menggunakan tatasusunan used tanpa indeks mula untuk menjana n! susunan, kombinasi menggunakan indeks mula yang dimajukan bagi mengelakkan guna semula dan menjana C(n,k) pemilihan, dan pendua dalam kedua-dua masalah dikendalikan dengan mengisih serta melangkau nilai berulang pada aras rekursi yang sama. Seterusnya, kita akan menggunakan penjejakan balik pada masalah N-Ratu dan meneroka perambatan kekangan.

Percuma untuk bermula

Pelajari Python dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
30
Pelajaran
120

Soalan Lazim

Adakah pelajaran “Permutasi dan Kombinasi” percuma?

Ya — sebanyak 3 pelajaran dalam laluan pembelajaran DSA Interview Prep, termasuk “Permutasi dan Kombinasi”, boleh dibaca sepenuhnya secara percuma di web ini. Selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan interaktif dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Kursus DSA Interview Prep merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Permutasi dan Kombinasi”?

Senaraikan semua permutasi bagi suatu senarai dengan dan tanpa unsur pendua, serta jana semua kombinasi-k dan variasi jumlah kombinasi. Anda berlatih DSA Interview Prep menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan DSA Interview Prep?

Tiada pengalaman terdahulu diperlukan. Pembelajaran DSA Interview Prep di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 3 daripada 4.

Berapa lamakah pelajaran “Permutasi dan Kombinasi” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran DSA Interview Prep ini?

Ya. Setiap pelajaran DSA Interview Prep menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Templat Jejak Balik: Pilih, Teroka, Nyahpilih
  2. Subset dan Set Kuasa
  3. Permutasi dan Kombinasi
  4. N-Queens dan Penyebaran Kekangan
← Kembali ke DSA Interview Prep