英文:
Strange behavior when appending array to matrix
问题
我对切片的一些基本知识似乎有所遗漏,导致我的结果看起来很奇怪。
是的,这是来自LeetCode的一个问题。我在使用它来学习Go语言,因为我发现在新语言中解决算法问题对我很有帮助。我不需要算法的答案,也不需要知道如何修复算法。我只想知道为什么当我追加另一个值时,我的追加值会改变。
首先,这是我的代码:
type node struct {
value int
children []*node
}
func combinationSum(candidates []int, target int) [][]int {
var output [][]int
var acc []int
combinationSum2(candidates, &output, target, acc, 0)
return output
}
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println(acc)
*output = append(*output, acc)
fmt.Println(output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
combinationSum2(candidates[i:], output, target, append(acc, candidates[i]), sum + candidates[i])
}
}
我使用candidates=[2,3,5]和target=8来测试这段代码。
正确的输出应该是[[2,2,2,2],[2,3,3],[3,5]];然而,我的代码返回[[2,2,2,5],[2,3,3],[3,5]]。
有趣的是,当我在if语句中记录acc值和在追加acc值后记录output时,似乎在追加第二个数组后,我追加的值发生了变化。
acc = [2 2 2 2]
output = &[[2 2 2 2]]
acc = [2 3 3]
output = &[[2 2 2 5] [2 3 3]]
acc = [3 5]
output = &[[2 2 2 5] [2 3 3] [3 5]]
我尝试在本地运行这段代码,结果得到了相同奇怪的行为。是什么导致了这个问题?
英文:
I'm missing something fundamental about slices that's causes my result to end up looking strange.
Yes this is a question from leetcode. I was using it to learn go because I find that solving algorithms in new languages is helpful to me. I don't need the answer to the algorithm and I don't need to know how to fix the algorithm. I just want to know why my appended value changes when I append another value.
First off here's my code:
type node struct {
value int
children []*node
}
func combinationSum(candidates []int, target int) [][]int {
var output [][]int
var acc []int
combinationSum2(candidates, &output, target, acc, 0)
return output
}
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if(sum == target) {
fmt.Println(acc)
*output = append(*output, acc)
fmt.Println(output)
return
}
if(sum > target) {
return
}
for i := 0; i < len(candidates); i++ {
combinationSum2(candidates[i:], output, target, append(acc, candidates[i]), sum + candidates[i])
}
}
I was testing this code with candidates=[2,3,5] and target=8
the correct output should be [[2,2,2,2],[2,3,3],[3,5]]; however, my code returns [[2,2,2,5],[2,3,3],[3,5]]
interestingly, when I log about the acc values in the if statement and the output after appending the acc value, it seems the the value that I appended changes after appending the second array.
acc = [2 2 2 2]
output = &[[2 2 2 2]]
acc = [2 3 3]
output = &[[2 2 2 5] [2 3 3]]
acc = [3 5]
output = &[[2 2 2 5] [2 3 3] [3 5]]
I tried running this locally, and get the same strange behavior. What's causing this?
答案1
得分: 1
正如我在评论中写的那样,原始代码中的问题在于combinationSum2
函数中的append
使用方式。append
被用来将当前的acc
扩展为当前的candidate
并传递给combinationSum2
方法。
我在这个函数中添加了更多的日志输出:
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println("Acc:", acc)
*output = append(*output, acc)
fmt.Println("Output:", output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
extendedAcc := append(acc, candidates[i])
fmt.Printf("Extended: %v %p\n", extendedAcc, extendedAcc)
combinationSum2(candidates[i:], output, target, extendedAcc, sum+candidates[i])
}
}
并收到了以下结果(这只是前几行有趣的输出):
Extended: [2] 0x14000130008
Extended: [2 2] 0x14000130030
Extended: [2 2 2] 0x1400013c020
Extended: [2 2 2 2] 0x1400013c020
Acc: [2 2 2 2]
Output: &[[2 2 2 2]]
Extended: [2 2 2 3] 0x1400013c020
Extended: [2 2 2 5] 0x1400013c020
Extended: [2 2 3] 0x1400013c040
Extended: [2 2 3 3] 0x1400013c040
正如你所看到的,extendedAcc
变量在添加到Output
之后仍然具有相同的地址(它们在值后面以十六进制打印出来)。这个地址的最终值是[2 2 2 5]
,这也是你在Output
中看到的。之所以不是[2 2 3 3]
,是因为append
的内部工作原理。当当前数组中没有足够的空间时,它会创建一个新的数组并返回一个指向它的切片。当你比较extended
切片的地址时,就能看到这种行为。
这是一个正常工作的解决方案:
package main
import "fmt"
type node struct {
value int
children []*node
}
func combinationSum(candidates []int, target int) [][]int {
var output [][]int
var acc []int
combinationSum2(candidates, &output, target, acc, 0)
return output
}
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println(acc)
*output = append(*output, acc)
fmt.Println(output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
currentAcc := make([]int, 0, len(acc)+1)
currentAcc = append(currentAcc, acc...)
currentAcc = append(currentAcc, candidates[i])
combinationSum2(candidates[i:], output, target, currentAcc, sum+candidates[i])
}
}
func main() {
combinationSum([]int{2, 3, 5}, 8)
}
或者,combinationSum2
函数可以像这样:
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println(acc)
accCopy := make([]int, 0, len(acc))
accCopy = append(accCopy, acc...)
*output = append(*output, accCopy)
fmt.Println(output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
combinationSum2(candidates[i:], output, target, append(acc, candidates[i]), sum+candidates[i])
}
}
在我看来,这种写法在一般情况下不太安全,但对于这个问题来说,它可以正常工作。
英文:
As i wrote in the comment the problem in original code is with append usage in combinationSum2
function. The append
was used to pass current acc
extended by current candidate
to combinationSum2
method.
I've added more logging to this function
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println("Acc:", acc)
*output = append(*output, acc)
fmt.Println("Output:", output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
extendedAcc := append(acc, candidates[i])
fmt.Printf("Extended: %v %p\n", extendedAcc, extendedAcc)
combinationSum2(candidates[i:], output, target, extendedAcc, sum+candidates[i])
}
}
and received following result (this is only few first interesting lines)
Extended: [2] 0x14000130008
Extended: [2 2] 0x14000130030
Extended: [2 2 2] 0x1400013c020
Extended: [2 2 2 2] 0x1400013c020
Acc: [2 2 2 2]
Output: &[[2 2 2 2]]
Extended: [2 2 2 3] 0x1400013c020
Extended: [2 2 2 5] 0x1400013c020
Extended: [2 2 3] 0x1400013c040
Extended: [2 2 3 3] 0x1400013c040
As you see the extendedAcc
variable after it was added to Output
still has the same address (they are printed as hex after the value). The final value for this address is [2 2 2 5]
which is what you see in Output
. The reason why it is not [2 2 3 3]
is the result of how append works internally. In case when there is not enough space in current array it creates new one and returns a slice to it. And this behavior is visible when you compare the addresses of extended
slice .
Here is properly working solution:
package main
import "fmt"
type node struct {
value int
children []*node
}
func combinationSum(candidates []int, target int) [][]int {
var output [][]int
var acc []int
combinationSum2(candidates, &output, target, acc, 0)
return output
}
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println(acc)
*output = append(*output, acc)
fmt.Println(output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
currentAcc := make([]int, 0, len(acc) + 1)
currentAcc = append(currentAcc, acc...)
currentAcc = append(currentAcc, candidates[i])
combinationSum2(candidates[i:], output, target, currentAcc, sum+candidates[i])
}
}
func main() {
combinationSum([]int{2, 3, 5}, 8)
}
Alternatively the combinationSum2
function may look like this:
func combinationSum2(candidates []int, output *[][]int, target int, acc []int, sum int) {
if sum == target {
fmt.Println(acc)
accCopy := make([]int, 0, len(acc))
accCopy = append(accCopy, acc...)
*output = append(*output, accCopy)
fmt.Println(output)
return
}
if sum > target {
return
}
for i := 0; i < len(candidates); i++ {
combinationSum2(candidates[i:], output, target, append(acc, candidates[i]), sum+candidates[i])
}
}
Which in my opinion is less safe in general but will work properly for this problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论