leetcode_組合總和2_2種解法

題目

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

Shoulders of Giants

題目來源於leetcode,解法來源於其下的評論區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章