使用Golang时,对深度优先搜索结果感到困惑

时间:2017-12-05 07:55:02

标签: go depth-first-search

我试图在leetcode上解决'组合和',使用测试用例时结果是错误的:

  

[7,3,2] 18

我使用相同的逻辑并传递了C ++,但是当使用Golang时,我的结果是:

  

[[2,2,2,2,2,2,2,2,2],[2,2,2,2,2,7,3,3],[2,2,2,2 ,3,7],[2,2,2,3,3,3,3],[2,2,7,7-],[2,3,3,3,7],[3,3,3- ,3,3,3-]

,正确的应该是

  

[[2,2,2,2,2,2,2,2,2],[2,2,2,2,2,2,3,3],[2,2,2,2 ,3,7],[2,2,2,3,3,3,3],[2,2,7,7-],[2,3,3,3,7],[3,3,3- ,3,3,3-]

代码如下所示:

import "sort"
func combinationSum(candidates []int, target int) [][]int {
    result := make([][]int, 0, 0)
    resultp := &result
    sort.Ints(candidates)
    helper(candidates, 0, target, make([]int, 0, 0), resultp, len(candidates))
    return *resultp
}

func helper(nums []int, index int, target int, list []int, resultp *[][]int, length int) {
    if target == 0 {
        *resultp = append(*resultp, list)
        return
    }
    for i := index; i < length; i++ {
        if i != index && nums[i] == nums[i - 1] {
            continue
        }
        if (nums[i] > target) {
            break
        }
        helper(nums, i, target - nums[i], append(list, nums[i]), resultp, length)
    }
}

任何人都可以告诉我为什么结果不正确,我只是对我的答案中的[2,2,2,2,2,7,3,3]感到困惑,为什么7在数组之前的3之前已经排序?或者任何人都可以告诉我在我的代码中犯了什么错误

4 个答案:

答案 0 :(得分:0)

如果list具有容量,那么它将被修改,因此您正在修改您的参数。而是复制列表,然后将nums[i]附加到其中。

请参阅Go Slices: usage and internals

答案 1 :(得分:0)

append函数可能会也可能不会修改切片引用的基础数组。因此,在使用追加时,您不会创建一个全新的列表。我更改了helper以符合您的期望行为。

for i := index; i < length; i++ {
    if i != index && nums[i] == nums[i - 1] {
        continue
    }
    if nums[i] > target {
        break
    }
    var newList []int
    newList = append(newList, list...)
    newList = append(newList, nums[i])
    helper(nums, i, target - nums[i], newList, resultp, length)
}

答案 2 :(得分:0)

该行

helper(nums, i, target - nums[i], append(list, nums[i]), resultp, length)

可能无法按预期执行。它在循环中调用,您可能假设在每次迭代中append将始终将新成员添加到现有切片中。如果你有更复杂的行为,你似乎不够关心:

  • 如果新值符合带有切片的后备阵列的当前容量,则会将其添加到当前后备阵列中。分配给该切片的所有变量现在都会报告更新的内容,并添加新值。
  • 如果值不合适,则分配新数组。在这种情况下,如果还保留旧值,则返回切片的进一步修改不会更改初始切片的内容。

我的印象是,您可能不会期望append返回的值与您传递给它的参数list之间的价值/内容不一致。

此行为描述为here(滚动到“gotcha”)。

答案 3 :(得分:0)

因此,您可以通过添加一些打印输出来更好地查看行为:

https://play.golang.org/p/JPmqoAJE4S

重要的是,您可以在此时看到它:

0694 helper [2 3 7] 1 1 [2 2 2 2 2 2 2 3] [[2 2 2 2 2 2 2 2 2]] 3
4425 calling down 1 6 [2 2 2 2 2 2] 3
8511 helper [2 3 7] 1 3 [2 2 2 2 2 2 3] [[2 2 2 2 2 2 2 2 2]] 3
8511 calling down 1 3 [2 2 2 2 2 2 3] 3
8162 helper [2 3 7] 1 0 [2 2 2 2 2 2 3 3] [[2 2 2 2 2 2 2 2 2]] 3
8162 solution [2 2 2 2 2 2 3 3] [[2 2 2 2 2 2 2 2 2] [2 2 2 2 2 2 3 3]]
1318 calling down 1 8 [2 2 2 2 2] 3
5089 helper [2 3 7] 1 5 [2 2 2 2 2 3] [[2 2 2 2 2 2 2 2 2] [2 2 2 2 2 3 3 3]] 3
5089 calling down 1 5 [2 2 2 2 2 3] 3
4728 helper [2 3 7] 1 2 [2 2 2 2 2 3 3] [[2 2 2 2 2 2 2 2 2] [2 2 2 2 2 3 3 3]] 3
1318 calling down 2 8 [2 2 2 2 2] 7
3274 helper [2 3 7] 2 1 [2 2 2 2 2 7] [[2 2 2 2 2 2 2 2 2] [2 2 2 2 2 7 3 3]] 3

这是一系列行动:

  • 您递归调用[2 2 2 2 2 2 3]并附加3.您会发现这是一个有效的解决方案并将[2 2 2 2 2 2 3 3]添加到结果切片。
  • 您返回几个级别,直到您回到[2 2 2 2 2](在添加第6个2之前)并开始尝试添加3个。您递归调用[2 2 2 2 2]并附加3.不幸的是,这会覆盖您现有的解决方案[2 2 2 2 2 2 3 3]。由于它使用相同的后备数组,因此将3附加到该切片中的前5个项目,覆盖先前添加到解决方案集中的切片中的第6个索引。您的第二个解决方案变为[2 2 2 2 2 3 3 3](请注意第6个插槽中的3
  • 您发现此解决方案集在经过几次迭代([2 2 2 2 2 3 3])后无法正常工作,因为剩余目标(2)小于最后添加的数字(3),因此您返回起来。
  • 在第6个插槽中用7重复此序列,再次覆盖基础数组索引。您的第二个解决方案变为[2 2 2 2 2 7 3 3],因为您使用相同的基础数组。你发现这个解决方案也不会起作用,然后返回。

在这一点之后,你返回到列表切片长度大于4之前(这是切片增长的时候,默认情况下,它的大小增加一倍),这意味着你要使用不同的(之前) )支持数组,这就是为什么进一步的迭代不会进一步改变现有解决方案的原因。幸运的是,剩下的解决方案都没有以类似的方式发生碰撞。

此替代打印版本显示支持数组更改的位置(通过显示第一个条目的地址更改的位置):https://play.golang.org/p/nrgtMyqwow。正如您所看到的,当您长度超过长度2,4和8时,它会发生变化,但是当您向上返回时,最终会恢复到不同的后备阵列。

解决特定问题的最简单方法是在将列表切片添加到解决方案集之前复制它:

if target == 0 {
    sol := make([]int, len(list))
    copy(sol, list)
    *resultp = append(*resultp, sol)
    return
}

https://play.golang.org/p/3qTKoAumj0

[[2 2 2 2 2 2 2 2 2] [2 2 2 2 2 2 3 3] [2 2 2 2 3 7] [2 2 2 3 3 3 3] [2 2 7 7] [2 3 3 3 7] [3 3 3 3 3 3]]