9-2

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

Python most votes solution:

class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        res = []
        candidates.sort()
        self.dfs(candidates, target, 0, [], res)
        return res

    def dfs(self, nums, target, start, path, res):
        if target == 0:
            res.append(path)
            return 
        for i in xrange(start, len(nums)):
            if target < nums[i]:
                break
            self.dfs(nums, target-nums[i], i, path+[nums[i]], res)

分析:

(1)代码用到了树结构中的DFS(深度优先搜索)。

(2)在代码最开始对candidates进行排序,是为了在递归中,一旦遇到不合适的情形,能够尽早地跳出循环,从而减少代码的运行时间。

(3)如何构建这个递归结构?首先要明白每一次递归,target都要减去nums中的一个数,从而在整个递归调用中,每一次更新后的target控制了程序下一步的导向:

  • 如果target == 0,要return;
  • 如果target < nums[i],就没有再往深处搜索的必要了(因为nums是有序的,深度优先搜索的顺序是由小到大的),所以此时程序应尽早退出循环,往广度的方向进行搜索。

(4)在每一轮递归中,要传递哪些参数?

  • 首先必须要有nums,因为我们在每一轮递归中都要用到nums中的数,而且在传递的过程中我们不能修改nums(因为程序要从最深的节点逐级返回);
  • 其次,必须要有target,它控制了程序下一步运行的方向。为了能够使target抵达递归基,同时代码采用的又是DFS策略,因此每次递归往深处进行时,target都要减去nums中的一个数(在满足条件的情况下是由最小的数减到最大的数,这一过程是由for循环实现的);
  • 再次,上一次递归的索引 i 也是要传递的,这可以避免不必要的搜索过程。例如,当程序已经搜索至 2 -> 3 时,利用索引 i 可以保证下一次搜索的数字是从3开始而不是从2开始的(因为在2 -> 3 -> 2之间,已经有过 2 -> 2 -> 3了,传递索引 i 可以避免这类重复搜索的情况);
  • 接着,要传递当前的搜索路径,在每次递归中,它也是动态变化的:每往下递归一层,它的路径中就增添一项;每往上回退一层,就回到先前的那个路径。传递该路径的目的是一旦找到符合题意的路径组合,就将它保存到res中。
  • 最后要传递res,它保存了所有满足要求的路径,用于最终函数结果的返回。
posted @ 2019-07-10 09:53  mingyu02  阅读(223)  评论(0编辑  收藏  举报