You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go中append的异常行为:LeetCode子集问题回溯解法答疑

Go回溯解法中子集问题的切片引用陷阱

问题场景

用Go解决子集问题时,写出了如下回溯解法,但结果不正确:

func subsets(nums []int) [][]int {
    sol := make([][]int,0)
    temp:= make([]int,0)

    var backtrack func(idx int)
    backtrack = func(idx int) {
        sol = append(sol, temp)
        fmt.Println(temp, append([]int{},temp...))

        if idx == len(nums) {
            return
        }

        for i:= idx; i<len(nums);i++{
            temp = append(temp,nums[i])
            backtrack(i+1)
            temp = temp[:len(temp)-1]
        }

    }
    backtrack(0)
    return sol
}

测试发现必须用append(sol, append([]int{}, temp...))替代sol = append(sol, temp)才能得到正确结果。虽然执行fmt.Println(temp, append([]int{}, temp...))时两者输出内容一致,但原写法无法得到正确结果,需要明确两者的区别及原因。


核心区别:引用 vs 独立副本

  • 直接存temp是存引用:Go的切片是包含「底层数组指针、长度、容量」的结构体。当你把temp追加到sol时,sol里存储的是这个切片结构体的副本,但它指向的底层数组和原temp完全相同。后续回溯过程中,temp会被append添加元素,又被temp[:len(temp)-1]截断,这些操作都会修改共享的底层数组内容。最终sol里所有之前存入的切片,都会指向被反复修改后的底层数组,导致结果全错。
  • append([]int{}, temp...)是创建独立副本:这个写法会先初始化一个空切片,再把temp的所有元素复制进去,生成一个全新的切片。新切片拥有独立的底层数组,和原temp彻底分离。后续修改temp的内容时,不会影响已经存入sol的这个副本,因此sol里的每个子集都能保持当时的元素状态,最终得到正确结果。

为什么fmt.Println输出看起来一致?

fmt.Println打印切片时,会即时读取切片当前指向的底层数组内容。执行fmt.Println(temp, append([]int{}, temp...))时,这两个操作在同一时间点完成,此时temp的底层数组还没被后续回溯步骤修改,所以两者输出的内容完全相同。但后续回溯修改temp时,sol里的原切片引用会同步变化,而副本不受影响。


内容的提问来源于stack exchange,提问作者yisic80

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:32:33