将数组附加到矩阵时的奇怪行为

时间:2021-08-01 21:31:26

标签: go

我遗漏了一些关于切片的基本知识,这导致我的结果看起来很奇怪。

是的,这是来自 leetcode 的问题。我用它来学习围棋,因为我发现用新语言解决算法对我很有帮助。我不需要算法的答案,也不需要知道如何修复算法。我只想知道当我附加另一个值时,为什么我的附加值会发生变化。

首先这是我的代码:

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])
    }
    
}

我用候选者=[2,3,5] 和目标=8 测试此代码

正确的输出应该是[[2,2,2,2],[2,3,3],[3,5]];但是,我的代码返回 [[2,2,2,5],[2,3,3],[3,5]]

有趣的是,当我记录 if 语句中的 acc 值和附加 acc 值后的输出时,我附加的值似乎在附加第二个数组后发生了变化。

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]]

我尝试在本地运行它,并得到相同的奇怪行为。这是什么原因造成的?

1 个答案:

答案 0 :(得分:1)

正如我在评论中所写,原始代码中的问题在于 combinationSum2 函数中的附加用法。 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 slice 的地址时,这种行为是可见的。

这是正常工作的解决方案:

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])
    }
}

在我看来,这通常不太安全,但可以正常解决这个问题。