题面
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
示例
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
回溯思想
回溯算法实际上一个类似枚举的深度优先搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,
当发现已不满足求解条件时,就“回溯”返回(也就是递归返回),尝试别的路径。
- 选择 有限个选择,或不选取
- 条件 不符合条件,就要回溯,即递归调用的返回
- 结束 把符合要求的解用全局变量存储,作为参数之一传递给递归函数
递归参数选择
- 必须要有一个临时变量参数,传递不完整解
- 通常用全局变量,存储完整解
- 在参数设计中,可以得到结束条件
- 确保递归函数返回后,状态可以恢复到递归前,以此达到真正回溯(在使用golang的append尤其要注意,其底层可能改变)
算法分析
去重
golang
append 切片陷阱
import "sort"
func combinationSum(candidates []int, target int) [][]int {
sort.Ints(candidates)
rs :=[][]int{}
var backtrack func(int,int,[]int)
backtrack =func(begin,target int,path []int){
if begin == len(candidates){
return
}
if target==0 {
t := make([]int,len(path))
copy(t,path)
rs = append(rs,t)
return
}
for i:=begin;i<len(candidates);i++{
if candidates[i] > target{
break
}
path = append(path,candidates[i])
backtrack(i,target-candidates[i],path)
path = path[:len(path)-1]
}
}
backtrack(0,target,[]int{})
return rs
}
python
py列表加法操作返回的是一个全新的副本列表
py基本类型传参时,是值复制
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(candidates, begin, path, rs, target):
if target == 0:
rs.append(path)
return
for index in range(begin, len(candidates)):
residue = target - candidates[index]
if residue < 0:
break
dfs(candidates, index, path + [candidates[index]], rs, residue)
if len(candidates) == 0:
return []
candidates.sort()
path = []
rs = []
dfs(candidates, 0, path, rs, target)
return rs
php
保证回溯状态一致,入参的临时不完全解变量与外层函数保持一致,即要求回溯后的不完全解状态与递归前外层不完解状态一样或相同
class Solution {
/**
* @param Integer[] $candidates
* @param Integer $target
* @return Integer[][]
*/
public $rs = [];
function combinationSum($candidates, $target) {
sort($candidates);
$this->candidates = $candidates;
$this->backTrace(0,[],$target);
return $this->rs;
}
function backTrace($idx,$path,$target){
if($i==count($this->candidates)){
return ;
}
if($target == 0){
array_push($this->rs, $path);
}
for($i=$idx;$i<count($this->candidates);$i++){
if($this->candidates[$i]> $target){
break;
}
$path = array_merge($path,[$this->candidates[$i]]);
$this->backTrace($i,$path, $target-$this->candidates[$i]);
array_pop($path);
}
}
}
n和挑k数
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
- 所有数字都是正整数。
- 解集不能包含重复的组合。
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解法分析
- 广度起始点
- 深度计数器
- 广度挑选去重,广度回溯状态重置
- 数据的有序性对去重十分重要
func combinationSum3(k int, n int) [][]int {
rs :=[][]int{}
var backtrace func(int,int,[]int)
backtrace = func(idx int, target int, path []int){
if idx == 9 {
return
}
if len(path) >= k {
if target == 0 {
t :=make([]int,k)
copy(t,path)
rs = append(rs,t)
}
return
}
for i:=idx;i<9;i++{
if target < i+1{
break
}
if len(path)>0 && path[len(path)-1]==i+1 {
continue
}
path = append(path, i+1)
backtrace(i,target-i-1,path)
path = path[:len(path)-1]
}
}
backtrace(0,n,[]int{})
return rs
}