題目
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
Each number in candidates may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
[1,2,2],
[5]
]
題目分析
- 如何利用遞歸列舉出合適的組合?
解法1
class Solution2:
def combinationSum2(self, candidates, target):
"""
代碼缺陷:在當前層無法和targ 比較大小
:param candidates:
:param target:
:return:
"""
def recursion(candis, targ):
if targ == 0:
return [[]] # 遞歸出口
if len(candis) == 0 or targ < 0:
return [] # 遞歸非正常出口
return [[candis[0]] + x for x in \
recursion(candis[1:], targ - candis[0])] + \
recursion([c for c in candis if c > candis[0]], targ)
return recursion(sorted(candidates), target)
解法1_Q&A
- 代碼1需要優化的地方:例如當前temp已經是[1,1,2,5],可是代碼仍然繼續比較剩下的數,構建出類似於[1,1,2,6] 的組合,這邊明明可以通過剪枝縮小進層數 NOTACK
recursion([c for c in candis if c > candis[0]], targ)
有何用
答:用來篩選不合適的數,例如當前temp已經是[1,1,2,5],可是代碼仍然繼續比較剩下的數,構建出類似於[1,1,2,6] 的組合,此時candis[0]=5- return [] 和return [[]]的區別
for i in []:
print('a',i) # print nothing
for i in [[]]:
print('a',i)
a []
解法2
class Solution:
def combinationSum2(self, candidates, target):
"""
:param candidates:
:param target:
:return:
"""
n = len(candidates)
candidates.sort()
res = []
def dfs(i, tmp_sum, tmp):
if tmp_sum > target:
return # 返回上一層
if tmp_sum == target:
res.append(tmp)
return # 返回上一層
for j in range(i, n):
if tmp_sum + candidates[j] > target: break # 解法1 需要優化的地方,因爲整體是有序的,如果此時大於target,那麼接下來的candidates 都不用比較
if j > i and candidates[j] == candidates[j - 1]: continue # 兩個條件共同作用下,去掉重複比較 同一位置
dfs(j + 1, tmp_sum + candidates[j], tmp + [candidates[j]])
dfs(0, 0, [])
return res
candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8
result = Solution2().combinationSum2(candidates, target)
print(result)
解法2_Q&A
-
爲啥for 循環中要再弄一個判斷是否大於target, 一個用來判斷每次進層的tmp_sum 是否大於target,另一個是用來判斷加上candidate[x] 後是否大於target
答:其實可以合併成一個,但有兩個判斷使算法更高效,不用等到進層後才判斷當前候選數是否合適 -
當遞歸退層到合適的位置,是否還會繼續前進
答:例如檢測組合[1,1,2,5]不成立,那麼遞歸退層到[1,1] 那麼此時應當是繼續前進到5,也就是構成[1,1,5]<得益於i 無論如何都會運行下去,該條件滿足> -
因爲每個數字只能在組合中使用一次,so 如何避免同一個數字被多次使用
答:if j > i and candidates[j] == candidates[j - 1]
剛開始i=0,j=0時,該語句不執行,當j>2時,因爲列表已經是有序的,so 只需要比較第i個數和第i-1個數的大小(可能會產生重複解,例如[1,1,2,3],target 爲4 會產生[[1,3],[1,1,2],[1,3]] 三種解 )即可<專業術語就是:相同遞歸深度的list 不會重複添加同一個數> NOTACK