Medium草稿★★★★★O(2^n) 時間 · O(n) 空間
BacktrackingArraySorting
Patterns🌿 回溯🔢 排序後處理
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

40Combination Sum IIBacktrackingMediumBacktracking

給定一個整數陣列 candidates(可能有重複)和目標值 target,找出所有 candidates 中數字之和等於 target 的唯一組合。每個數字只能使用一次。

Example:

Input: candidates = [10,1,2,7,6,1,5], target = 8 Output: [[1,1,6],[1,2,5],[1,7],[2,6]]

Intuition

TIP

核心思路:Combination Sum I + Subsets II 的結合 — 每個元素只用一次,且需要去除重複組合。

Approaches

1. Backtracking + HashSet Dedup — O(2^n) / O(n * 2^n)
  • Idea: 用 HashSet 存儲組合結果,避免重複
  • Time: O(2^n)
  • Space: O(n * 2^n)
class Solution {
    fun combinationSum2(candidates: IntArray, target: Int): List<List<Int>> {
        candidates.sort()
        val result = HashSet<List<Int>>()
        val current = mutableListOf<Int>()

        fun backtrack(start: Int, remain: Int) {
            if (remain == 0) {
                result.add(ArrayList(current))
                return
            }

            for (i in start until candidates.size) {
                if (candidates[i] > remain) break
                current.add(candidates[i])
                backtrack(i + 1, remain - candidates[i])
                current.removeAt(current.lastIndex)
            }
        }

        backtrack(0, target)
        return result.toList()
    }
}
⭐ 2. Backtracking + Skip Same Level — O(2^n) / O(n)
  • Idea: 排序後,同層跳過重複元素,每個元素只用一次(遞迴 i+1),加上排序剪枝
  • Time: O(2^n)
  • Space: O(n) - 遞迴深度
class Solution {
    fun combinationSum2(candidates: IntArray, target: Int): List<List<Int>> {
        candidates.sort()
        val result = mutableListOf<List<Int>>()
        val current = mutableListOf<Int>()

        fun backtrack(start: Int, remain: Int) {
            if (remain == 0) {
                result.add(ArrayList(current))
                return
            }

            for (i in start until candidates.size) {
                if (candidates[i] > remain) break
                if (i > start && candidates[i] == candidates[i - 1]) continue  // 同層跳過
                current.add(candidates[i])
                backtrack(i + 1, remain - candidates[i])
                current.removeAt(current.lastIndex)
            }
        }

        backtrack(0, target)
        return result
    }
}

🔑 Takeaways

Ladders 看全部 →

🎚️

子集與組合(回溯)

同一回溯模板,差別在去重與選取規則。

  1. 1#78SubsetsMedium所有子集:選/不選的決策樹
  2. 2#90Subsets IIMedium含重複元素 → 同層跳過重複
  3. 3#77CombinationsMedium固定長度 k 的組合
  4. 4#39Combination SumMedium元素可重複使用,湊出目標和
  5. 5