一.问题描述
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
- All numbers (including target) will be positive integers.
- The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7]
and target 7
,
A solution set is:
[ [7], [2, 2, 3] ]即给定一个int的list,即一个目标和target,找出list中所有和为target的int值组合,其中list中的int可以重复使用,也可以不按顺序,但是返回值中不能有重复的组合。
二.代码编写
看到这种题型我的第一反应是dp,但是dp并不适合这种要返回所有具体组合的题,(dp更适合返回是否存在这样的组合)。个人认为,因为dp是用空间换取时间,但这里需要存储的组合数过多。
这种题是典型的DFS的思想,想到DFS就一定要想到回溯,实际上回溯就是DFS加上剪枝的思想(剪枝就是当不满足条件的时候及时回退,不继续往下遍历)。
这个题目要注意的是停止条件的确定,用到栈的思想,先入栈不满足条件的时候将其弹出。
我们始终
代码编写上要注意的是,我们用list_a存储当前的栈,一旦满足条件(==target)时要将其append到输出list中,由于list是可变类型,所以赋值的时候要用到deepcopy()。
代码如下:
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
# backtracking
list_a = []
return_list = []
i = 0
# step1: sort the candidates
candidates.sort()
sum = 0
while i < len(candidates) or list_a!=[]: # stopping condition
while i >= len(candidates): # when this way going to an end
if list_a == []: # when list_a is None and i>=len(Candidates), then stop searching
break
aaa = list_a.pop() # then up backtracking
sum -= aaa
i = candidates.index(aaa)+1
if i>=len(candidates): # stop searching
break
sum += candidates[i]
list_a.append(candidates[i]) # add it to the stack
if sum > target: # backtracking
ind = list_a.pop()
sum -= ind
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
#i += 1
i = candidates.index(ind)+1
#i = candidates.index(list_a[-1])
#i += 1
elif sum == target:
#list_a.append(candidates[i])
return_list.append(copy.deepcopy(list_a)) # dont append list_a directly!!!
ind = list_a.pop()
sum -= ind
#i += 1
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
#list_a.pop()
#i += 1
i = candidates.index(ind)+1
'''
else:
sum += candidates[i]
list_a.append(candidates[i])
'''
'''
sum = 0
list_b = []
while candidates[-1]!=target and sum <= target:
list_b.append(candidates[-1])
sum += candidates[-1]
if sum == target and (return_list==[] or return_list[-1]!=list_b):
return_list.append(copy.deepcopy(list_b))
break
'''
return return_list
基本上就是树的DFS(Depth First Search),回溯的时候网上回溯两个节点,最上面形式化了一个头结点。
三.CombinationSumII
这一题和39的不同之处在于每个元素只能使用一次,当然了,不是说在整个寻找过程中只能用一次,而是说在一个结果中不能重复使用。
实现的注意点:
1)每次循环指针i加1;
2)有重复元素,index的时候要index到最后一个(python对list没有rindex,所以自定义实现了)
3)一种特殊情况(candidates = [3,1,3,5,1,1], target = 8),存在一种情况就是分支走到最后一个元素,sum仍然小于target,这时候的回溯要直接回溯到head,否则会不停循环,TLE
代码:
'''
@ author: wttttt at 2016.11.19
@ problem description see: https://leetcode.com/problems/combination-sum-ii/
@ solution explanation see: http://blog.youkuaiyun.com/u014265088/article/details/53138838
@ backtracking(pruned dfs)
'''
import copy
class Solution(object):
# the function used to find the last targeted element in the list, somewhat like rindex() for str.
def findlast(self,list_test,tar):
i = 0
while tar in list_test[i:]:
i += list_test[i:].index(tar)
i += 1
return i-1
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
# backtracking
list_a = []
return_list = []
i = 0
# step1: sort the candidates
candidates.sort()
sum = 0
while i < len(candidates) or list_a != []: # stopping condition
# another difference contrast to leetcode39 is:
# one conditon that the sumii should notice is that because u can just use each element once,
# so that u may backtracking when sum<target, at this situation, u should pop() all the element in list_a.
while i >= len(candidates) and list_a!=[]: # when this way going to an end
if list_a == []: # when list_a is None and i>=len(Candidates), then stop searching
break
aaa = list_a.pop() # then up backtracking
sum -= aaa
i = self.findlast(candidates,aaa) + 1
if i >= len(candidates): # stop searching
break
sum += candidates[i]
list_a.append(candidates[i]) # add it to the stack
if sum > target: # backtracking
ind = list_a.pop()
sum -= ind
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
i = self.findlast(candidates,ind)
elif sum == target:
# list_a.append(candidates[i])
return_list.append(copy.deepcopy(list_a)) # dont append list_a directly!!!
ind = list_a.pop()
sum -= ind
# i += 1
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
i = self.findlast(candidates, ind)
# main difference contrast to leetcode39
i += 1
return return_list
So = Solution()
candi = [3,1,3,5,1,1]
target = 8
print So.combinationSum2(candi,target)
#print So.findlast([1,1,2,5,6,7,10],1)