Leetcode 刷题第25天 |40

Leetcode 40 组合总和2

题目:

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 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]
]
提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

算法思想:

首先要明确本题与组合总和 和 组合总和3的差别。
与组合总和:解集中元素可重复,不限制元素个数。那么下一次迭代范围就是从index开始,而不是从startIndex开始。为什么?元素可重复,为什么不是每次都从1开始呢?如果从1开始,那么必然要包含重复元素。

与组合总和3:解集中无重复元素,限制元素个数。
以上两种方法候选集中没有重复元素。

采用回溯算法。但是给的候选集中可以有重复元素。这是与上两道题的本质区别。

  • 回溯函数模板返回值以及参数
    path:记录当前路径上的节点
    result:记录所有满足条件的组合
    startIndex:每个数字在答案中只能使用一次
  • 回溯函数终止条件
    当path的所有元素的和恰好为target的时候,结束。(可包含剪枝)
  • 单层搜索逻辑
    path中加入节点
    递归加入下一个节点
    path中出栈
    注意避免重复元素,重复元素的来源是题目给的candidates中含有重复元素,而两个重复元素还可以出现在答案结果中。例如:candidates为[1,1,2],target为3的时候,当第一次第一个数取1的时候,第二个数可以取[1,2]。当第二次第一个数取1的时候,第二个数只能取2,此时就包含重复的情况,需要去重。所以综上所述,存在着两种重复的情况,第一种是沿着树枝一直到叶子节点,这样即使有重复元素,也是不同元素。第二种是同一层的相同元素,这样就是重复的,因为前边的元素包含了后边重复元素的所有情况。因此,代码实现中借助used数组来记录元素是否访问过。

代码实现:

import copy

class Solution:
    def backtracing(self, candidates, target, path, result, startIndex, used):
        if sum(path) > target:  #  在这里的话,相当于多递归了一次
            return
        if sum(path) == target:
            result.append(copy.deepcopy(path))
            return 
        for i in range(startIndex, len(candidates)):
            if i > 0 and candidates[i] == candidates[i-1] and used[i-1] == False:
                continue
            # 在这里判断当前path里元素和 与 新加入元素的和是否大于 target,相当于是少递归了一个层次
            path.append(candidates[i])
            used[i] = True
            self.backtracing(candidates, target, path, result, i+1, used)
            path.pop()
            used[i] = False

    def combinationSum2(self, candidates, target: int):
        used = [False] * len(candidates)
        path = []
        result = []
        startIndex = 0
        candidates.sort()
        self.backtracing(candidates, target, path, result, startIndex, used)
        return result
    
import unittest

class CombinationSum2Test(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_combinationSum2(self):
        candidates = [10, 1, 2, 7, 6, 1, 5]
        target = 8
        expected = [[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]]
        result = self.solution.combinationSum2(candidates, target)
        self.assertEqual(result, expected)

        # candidates = [2, 5, 2, 1, 2]
        # target = 5
        # expected = [[1, 2, 2], [5]]
        # result = self.solution.combinationSum2(candidates, target)
        # self.assertEqual(result, expected)

        # candidates = [1, 2, 3]
        # target = 4
        # expected = [[1, 3]]
        # result = self.solution.combinationSum2(candidates, target)
        # self.assertEqual(result, expected)

if __name__ == '__main__':
    unittest.main()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值