先画图,写出递归树:
有点乱不好意思
通过图可以观察到for循环从底从左往右遍历,然后递归深度。
backtracking的设计应该是
1、设计递归出口,并处理逻辑
递归出口刚开是想到的是startIndex=len(s),但是发现不太合理,如果是这个递归出口的话那么会一直循环一直递归,会导致栈溢出,后来仔细想了一下应该是target==0的时候刚好得到一组数据。所以应该是target==0
if target == 0 {
temp := make([]int, len(paths)) // 创建一个临时切片存储当前路径
copy(temp, paths) // 拷贝当前路径到临时切片
*res = append(*res, temp) // 将临时切片加入结果数组中
return
}
原来我直接将path append到res中了,但是在第二个测试用例中出现了异常结果:[2,2,3,2],仔细查找了一下原因是因为切片是引用类型,在回溯过程中,paths 会不断地被修改和回溯,如果直接将 paths 添加到结果数组中,后续对 path 的修改也会影响到已经添加到结果数组中的路径。为了避免这个问题,我们需要创建一个临时切片 temp,将当前的路径 path 拷贝到 temp 中,然后将 temp 添加到结果数组中。这样可以确保每个路径在添加到结果数组之前都是独立的,互不影响。
# 2、处理横向遍历for循环逻辑
在横向处理for中逻辑的时候就比较常规了对横向的字符串数字遍历就好,图中可以观察到只要当前节点的值不大于target就可加入路径,但是值得注意的是本题中数字是可以一直使用的。所以在if语句块内回溯的时候就不能对index+1回溯了,需要对当前的i回溯,因为可以无限使用。
func backtracking(candidates []int, index int, target int, paths []int, res *[][]int) {
if target == 0 {
temp := make([]int, len(paths)) // 创建一个临时切片存储当前路径
copy(temp, paths) // 拷贝当前路径到临时切片
*res = append(*res, temp) // 将临时切片加入结果数组中
return
}
for i := index; i < len(candidates); i++ {
if candidates[i] <= target {
paths = append(paths, candidates[i])
backtracking(candidates, i, target-candidates[i], paths, res)
paths = paths[:len(paths)-1]
}
}
}
# 3、设计主函数:
开始的时候我是用的全局变量 res,并尝试通过返回值的方式来返回结果,这样会导致出现不可预料的结果。后来我将 res 作为参数传递给 backtracking 函数,并且将它修改为指针类型。然后,在 combinationSum 函数中,直接声明一个局部的结果数组并将其作为参数传递给 backtracking 函数。这样就避免问题了。
贴上全部代码:
package main
import "fmt"
func combinationSum(candidates []int, target int) [][]int {
res := [][]int{}
startIndex := 0
backtracking(candidates, startIndex, target, []int{}, &res)
return res
}
func backtracking(candidates []int, index int, target int, paths []int, res *[][]int) {
if target == 0 {
temp := make([]int, len(paths)) // 创建一个临时切片存储当前路径
copy(temp, paths) // 拷贝当前路径到临时切片
*res = append(*res, temp) // 将临时切片加入结果数组中
return
}
for i := index; i < len(candidates); i++ {
if candidates[i] <= target {
paths = append(paths, candidates[i])
backtracking(candidates, i, target-candidates[i], paths, res)
paths = paths[:len(paths)-1]
}
}
}
func main() {
candidates := []int{2, 3, 5}
target := 8
result := combinationSum(candidates, target)
fmt.Println(result)
}