在Go语言中的组合总和问题

huangapple go评论112阅读模式
英文:

Combination Sum in Go

问题

给定一个数组:[1,2]和一个目标值:4
找到总和等于目标值的解集
在这种情况下:
[1,1,1,1]
[1,1,2]
[2,2]

这是Leetcode上的一个问题,我使用递归来解决它。

在第18行,当总和等于目标值时,我打印出每种情况。
输出结果为:

[1,1,1,1]
[1,1,2]
[2,2]

这就是我想要的答案!
但为什么最终的答案(二维数组)是:

[[1,1,1,2],[1,1,2],[2,2]]

期望的答案是:[[1,1,1,1],[1,1,2],[2,2]]

请帮我找出代码中的错误。谢谢你的时间。

英文:
  1. /*
  2. Given an array: [1,2] and a target: 4
  3. Find the solution set that adds up to the target
  4. in this case:
  5. [1,1,1,1]
  6. [1,1,2]
  7. [2,2]
  8. */
  9. import "sort"
  10. func combinationSum(candidates []int, target int) [][]int {
  11. sort.Ints(candidates)
  12. return combine(0, target, []int{}, candidates)
  13. }
  14. func combine(sum int, target int, curComb []int, candidates []int) [][]int {
  15. var tmp [][]int
  16. var result [][]int
  17. if sum == target {
  18. fmt.Println(curComb)
  19. return [][]int{curComb}
  20. } else if sum < target {
  21. for i,v := range candidates {
  22. tmp = combine(sum+v, target, append(curComb, v), candidates[i:])
  23. result = append(result,tmp...)
  24. }
  25. }
  26. return result
  27. }

This is a problem in Leetcode and I use recursion to solve it.

In line 18, I print every case when the sum is equal to the target.
The output is :

  1. [1,1,1,1]
  2. [1,1,2]
  3. [2,2]

And that is the answer that I want!
But why is the final answer (two-dimensional):

  1. [[1,1,1,2],[1,1,2],[2,2]]

Expected answer is : [[1,1,1,1],[1,1,2],[2,2]]

Please help me find the mistake in the code. Thanks for your time.

答案1

得分: 2

这是因为切片的工作方式导致的。切片对象是对底层数组的引用,同时包含切片的长度、切片在数组中的起始位置的指针以及切片的容量。切片的容量是从切片开始到数组末尾的元素数量。当你向切片追加元素时,如果有足够的容量可以存放新元素,它会被添加到现有的数组中。然而,如果容量不足,append 函数会分配一个新的数组并将元素复制过去。新数组会分配额外的容量,以避免每次追加都需要重新分配内存。

在你的 for 循环中,当 curComb[1, 1, 1] 时,它的容量是 4。在循环的连续迭代中,你追加了 1 和 2,由于数组中有足够的空间来容纳新元素,所以不会触发重新分配内存。当 curComb 变为 [1, 1, 1, 1] 时,它被放入结果列表中,但在下一次 for 循环的迭代中,append 函数会修改最后一个元素为 2(记住它们共享同一个底层数组),所以在最后打印结果时你看到的是 [1, 1, 1, 2]

解决这个问题的方法是在和等于目标值时返回 curComb 的副本:

  1. if sum == target {
  2. fmt.Println(curComb)
  3. tmpCurComb := make([]int, len(curComb))
  4. copy(tmpCurComb, curComb)
  5. return [][]int{tmpCurComb}
  6. }

这篇文章 对切片的工作原理进行了很好的解释。

英文:

This happens because of the way slices work. A slice object is a reference to an underlying array, along with the length of the slice, a pointer to the start of the slice in the array, and the slice's capacity. The capacity of a slice is the number of elements from the beginning of the slice to the end of the array. When you append to a slice, if there is available capacity for the new element, it is added to the existing array. However, if there isn't sufficient capacity, append allocates a new array and copies the elements. The new array is allocated with extra capacity so that an allocation isn't required for every append.

In your for loop, when curComb is [1, 1, 1], its capacity is 4. On successive iterations of the loop, you append 1 and then 2, neither of which causes a reallocation because there's enough room in the array for the new element. When curComb is [1, 1, 1, 1], it is put on the results list, but in the next iteration of the for loop, the append changes the last element to 2 (remember that it's the same underlying array), so that's what you see when you print the results at the end.

The solution to this is to return a copy of curComb when the sum equals the target:

  1. if sum == target {
  2. fmt.Println(curComb)
  3. tmpCurComb := make([]int, len(curComb))
  4. copy(tmpCurComb, curComb)
  5. return [][]int{tmpCurComb}

This article gives a good explanation of how slices work.

huangapple
  • 本文由 发表于 2017年4月1日 23:59:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/43158984.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定