給定一個可能包含重複數字的整數數組 candidates
和一個目標值 target
,找出所有的唯一組合,使得這些組合中的數字和為 target
。
數組中的每個數字只能在每個組合中使用一次。
範例 1
輸入: candidates = [10,1,2,7,6,1,5], target = 8
輸出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
範例 2
輸入: candidates = [2,5,2,1,2], target = 5
輸出:
[
[1,2,2],
[5]
]
這是一個典型的回溯法(Backtracking)問題,但與 39. Combination Sum 不同的是,這裡的數組中可能包含重複元素,我們需要特別處理去重問題。
target
小於 0 時,停止遞迴。class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
def backtrack(start, target, path):
if target == 0:
result.append(list(path)) # 找到一個組合
return
if target < 0:
return # 剪枝
for i in range(start, len(candidates)):
# 跳過同層中重複的數字
if i > start and candidates[i] == candidates[i - 1]:
continue
path.append(candidates[i]) # 選擇當前數字
backtrack(i + 1, target - candidates[i], path) # 遞迴進入下一層
path.pop() # 撤銷選擇
candidates.sort() # 排序
result = []
backtrack(0, target, [])
return result
n
為候選數組的長度。最壞情況下,我們需要探索所有可能的組合。n
是遞迴深度(對應目標值 target
)。雖然回溯法已經是該問題的主流解法,但我們可以嘗試用動態規劃解決。
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
dp = [set() for _ in range(target + 1)]
dp[0].add(()) # 和為 0 的組合為空元組
for num in candidates:
for j in range(target, num - 1, -1):
for comb in dp[j - num]:
dp[j].add(comb + (num,))
return [list(comb) for comb in dp[target]]
n
是候選數組的長度,target
是目標值。dp
陣列。