您的位置:首页 > 其它

leetcode -- Combination Sum -- 重点

2015-12-14 12:52 267 查看
https://leetcode.com/problems/combination-sum/

思路1 回溯

这里参照dfs tree2 组合树的建树方法,因为有重合数字,所以子节点不再是只是A[k]后面的元素,子节点应该还是原来所有的元素。

参考回溯的模板,跟dfs binary tree 模板不一样/article/1607287.html

例如:参考/article/4879769.html

比如对于数组3,2,6,7,target = 7,对数组排序得到[2,3,6,7]

1、第1个数字选取2, 那么接下来就是解决从数组[2,3,6,7]选择数字且target = 7-2 = 5

2、第2个数字选择2,那么接下来就是解决从数组[2,3,6,7]选择数字且target = 5-2 = 3

3、第3个数字选择2,那么接下来就是解决从数组[2,3,6,7]选择数字且target = 3-2 = 1

4、此时target = 1小于数组中的所有数字,失败,回溯,重新选择第3个数字

5、第3个数字选择3,那么接下来就是解决从数组[2,3,6,7]选择数字且target = 3-3 = 0

6、target = 0,找到了一组解,继续回溯寻找其他解

/article/2219400.html给出了比较详细的思路

这种方法好理解,但是效率低,可以改进。ref里面提到了改进方法,要先sort数组,why?现在还没太理解

class Solution(object):

def dfs(self, candidates, target, knums, solutions):
sum_knums = sum(knums)
if sum_knums == target :
return knums

for i in candidates:
all_sum = sum_knums + i
if all_sum <= target:
res = self.dfs(candidates, target, knums + [i], solutions)
if res:
res.sort()
if res not in solutions:
solutions.append(res)

def combinationSum(self, candidates, target):
res = []
self.dfs(candidates, target, [], res)
return res


思路2 回溯改进,去重复

因为思路1中的结果可能有重复,所以为了消除重复结果,可以按照/article/2219400.html 说的先排序,然后只搜索比当前搜索节点大的子节点。为什么要排序呢?其实没必要排序,对于当前节点A[i], 只需要搜索A[j], j>i即可,照样可以消除重复结果,跟求组合数或者子集的思路一样。只不过 一开始sort之后,最后回溯的结果就不需要再sort一下再存入solution了。所以最好还是一开始就排序。此外,还可以方便剪枝, 因为如果加上当前子节点之后的sum以及超过了target,那么就不用再继续搜索后续节点了,因为任意后续节点都更使得sum更大。这里暂时不考虑排序

参考回溯模板:

void dfs(int 当前状态)
{
if(当前状态为边界状态)
{
记录或输出
return;
}
for(i=0;i<n;i++)      //横向遍历解答树所有子节点
{
//扩展出一个子状态。
修改了全局变量//例如存储路径的stack。这里其实可以放到dfs之前
if(子状态满足约束条件)//求出current candidates
{
dfs(子状态)
}
恢复全局变量//回溯部分例如存储路径的stack
}
}


以下是程序

class Solution(object):

def dfs(self, candidates, start, end, target, knums, solutions):
sum_knums = sum(knums)
if sum_knums == target :
return knums

i = start
#print 'start = %d' %start
while i < end:
all_sum = sum_knums + candidates[i]
#print (i, all_sum)
if all_sum <= target:
res = self.dfs(candidates, i, end, target, knums + [candidates[i]], solutions)
if res:
res.sort()
#if res not in solutions:
solutions.append(res)
i += 1

def combinationSum(self, candidates, target):
res = []
#candidates.sort()#这里不需要排序
self.dfs(candidates, 0, len(candidates), target, [], res)
return res


这里需要注意的就是 knum其实就是stack的作用,这里 knums + [candidates[i]]相当于复制了一分新的搜索路径,所以不需要像模板一样,修改了全局变量,再恢复全局变量

res = self.dfs(candidates, i, end, target, knums + [candidates[i]], solutions)


此外,在存储最后结果的时候

solutions.append(res)


最好是复制res,即

solutions.append(res[:])


思路3 标准 回溯,原数组排序

首先排序输入数组,因为这样每个可能的解knums都是排好序的,否则每次当sum_knums == target的时候,还要对knums进行sort之后才能放到solution里面。另外排序还有利于剪枝,提高效率

这个code效率很低

class Solution(object):

def dfs(self, candidates, start, end, target, knums, solutions):

if sum(knums) == target:
solutions.append(knums[:])
return
else:
i = start
while i < end:
knums.append(candidates[i])
if sum(knums) <= target:
self.dfs(candidates, i, end, target, knums, solutions)
else:#这里就是排序之后的剪枝,不需要继续搜后续节点
knums.pop()
return
knums.pop()
i += 1

def combinationSum(self, candidates, target):
res = []
self.dfs(sorted(candidates), 0, len(candidates), target, [], res)
return res


为了简便,还可以去掉stack,直接改成

def dfs(self, candidates, start, end, target, knums, solutions):

if sum(knums) == target:
solutions.append(knums[:])
return
else:
i = start
while i < end:
if sum(knums) + candidates[i] <= target:
self.dfs(candidates, i, end, target, knums + [candidates[i]], solutions)
else:#这里就是排序之后的剪枝,不需要继续搜后续节点
return
i += 1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: