Given a set of candidate numbers (candidates
) (without duplicates) and a target number (target
), find all unique combinations in candidates
where the candidate numbers sums to target
.
The same repeated number may be chosen from candidates
unlimited number of times.
Note:
- All numbers (including
target
) will be positive integers. - The solution set must not contain duplicate combinations.
Example 2:Input: candidates = [2,3,5],
target = 8, A solution set is: [ [2,2,2,2], [2,3,3], [3,5] ]
假设符合条件的集合长度为N,首先考虑第一位,第一位可以取值nums所有元素,统计r中已有元素的和,
当s==target时记录此时r,返回即可,
当s>target返回即可
否则说明s<target 则应继续寻找下一个元素,使用start来标识当前已经到那个数字作为最终结果的开始寻找点。
以2, 3, 6, 7为例,第一个数字可以取2,3,6,7
第一个数字取2时,第二个数字 2, 3, 6, 7
第一个数字取3时,第二个数字 3, 6, 7 如果第二个数字从第一个数字开始取,则会产生重复,因此应该从第二个数字开始取。
class Solution:
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
rr=[]
candidates.sort()
self.backtrace(rr, [], 0, candidates, target, 0)
return rr
def backtrace(self, rr, r, s, candidates, target, start):
if s==target:
rr.append(r.copy())
return
if s>target:
return
for i in range(start, len(candidates)):
r.append(candidates[i])
s += candidates[i]
self.backtrace(rr, r, s, candidates, target, i)
s -= candidates[i]
r.pop(-1)
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] ]
每个数字只能用一次,将数组升序排列,这样相同的数字便会相邻,r中存放所有可能的试探组合
考虑第一位数字,可以放入数组所有数字,然后依据s与target来进行判断
若s<target 考虑第二位数字,首先若i>start and nums[i-1]==nums[i]说明r中已经取过nums[i-1]的值,因此nums[i]可以直接跳过,否则会重复,进行下一次遍历从start+1开始,每一个数字只能用一次,依次不能继续从start开始。
class Solution:
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
rr=[]
candidates.sort()
self.backtrace(rr, [], 0, candidates, target, 0)
return rr
def backtrace(self, rr, r, s, candidates, target, start):
if s == target:
rr.append(r.copy())
return
if s > target:
return
for i in range(start, len(candidates)):
if i>start and candidates[i-1]==candidates[i]:
continue
r.append(candidates[i])
#print(r)
s += candidates[i]
#print(s)
self.backtrace(rr, r, s, candidates, target, i+1)
r.pop(-1)
s -= candidates[i]
https://leetcode.com/problems/combination-sum-iii/
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
Note:
- All numbers will be positive integers.
- The solution set must not contain duplicate combinations.
Example 1:Input: k = 3, n = 7 Output: [[1,2,4]]
回溯法 最终集合长度为k,和为7 考虑第一位取值范围 1--9 当第一位确定时,第二位的取值是除去第一位的1--9剩余数字
遍历顺序
1 12 123 1234 12345 .... 8 89 9
回溯法提供了对于集合的某种遍历顺序,每取一种组合方式,都要对于该种取值集合进行判断是否符合条件,符合直接保存返回,若已超出临界条件并且继续考虑下一位肯定不符合条件则直接返回,如果条件达不到则进行下一位的考虑,增加集合r的取值,进行递归判断。
class Solution:
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
rr=[]
self.backtrace(rr, [], 0, k, 1, n)
return rr
def backtrace(self, rr, r, s, k, start, n):
if len(r)==k and s==n:
rr.append(r.copy())
return
for i in range(start, 10):
r.append(i)
s += i
#print(r, i, s)
self.backtrace(rr, r, s, k, i+1, n)
r.pop(-1)
s -= i
优化如下:加入提前终止条件:
class Solution:
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
rr=[]
self.backtrace(rr, [], 0, k, 1, n)
return rr
def backtrace(self, rr, r, s, k, start, n):
if len(r)==k and s==n:
rr.append(r.copy())
return
if s>n:
return
if len(r)>k:
return
if len(r)==k and s!=n:
return
for i in range(start, 10):
r.append(i)
s += i
#print(r, i, s)
self.backtrace(rr, r, s, k, i+1, n)
r.pop(-1)
s -= i
https://leetcode.com/problems/combination-sum-iv/
Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Example:
nums = [1, 2, 3] target = 4 The possible combination ways are: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1) Note that different sequences are counted as different combinations. Therefore the output is 7.
回溯法,第一位可能出现nums的每一种情况,第二位也可能出现nums的每一种情况。
class Solution:
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
total = [0]
self.backtrace(total, [], nums, 0, target)
return total[0]
def backtrace(self, total, r, nums, s, target):
if s==target:
total[0] += 1
return
if s>target:
return
for i in range(len(nums)):
r.append(nums[i])
s += nums[i]
#print(r, s)
self.backtrace(total, r, nums, s, target)
r.pop(-1)
s -= nums[i]
代码超时,这样需要遍历所有情况。
nums=[1,2,3] target=4
假定组成target=4的组合最后一位数字为nums[2]=3,则 则该种情况有 comb(target-nums[2])
同理 可得 comb(4)=comb(target-nums[2])+comb(target-nums[1])+comb(target-nums[0])
comb[target] = sum(comb[target - nums[i]]), where 0 <= i < nums.length, and target >= nums[i]
.
class Solution:
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if target==0:
return 1
res = 0
for i in range(len(nums)):
if target>=nums[i]:
res += self.combinationSum4(nums, target-nums[i])
return res
还是超时,这里面重复计算多次,因此可以用dp来保存中间结果,避免重复计算
生成一个长度为target+1的数组来保存组合数为某一值的数量comb
依次计算i=0,1,2.....target的种类数
对于每一种i==target,都需要遍历一遍给定数组nums
comb[i]=comb[i]+comb[i-nums[j]]
初始时comb[0]==1,最终返回comb[target]即可
class Solution:
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
comb = [0 for i in range(target+1)]
comb[0] = 1
for i in range(1, target+1):
res = 0
for j in range(len(nums)):
if i>=nums[j]:
res = res + comb[i-nums[j]]
#print(res, i, j)
#print(res, comb)
comb[i] = res
return comb[target]
https://leetcode.com/problems/palindrome-partitioning/
剑指offer回溯法题目总结
回溯法总结