发现问题:
当用golang刷一道LeetCode题目时,78.子集,发现数据始终对不上,打印数据发现,竟然有数据重复
错误代码:
var ans [][]int
func dfs(nums []int, nowNums []int, l, r int) {
ans = append(ans, nowNums)
if l == r {
return
}
for i := l; i < r; i++ {
dfs(nums, append(nowNums, nums[i]), i+1, r)
}
}
func subsets(nums []int) [][]int {
ans = [][]int{}
lenNums := len(nums)
dfs(nums, []int{}, 0, lenNums)
return ans
}
排查问题发现,nowNums切片在appen进ans后,竟然还发生了改变,导致原先不同数据变为相同
问题分析:
当dfs的形参用这种时
dfs(nums, append(nowNums, nums[i]), i+1, r)
等价与下面这种
x := append(nowNums, nums[i])
dfs(nums, x, i+1, r)
我们都知道,切片是会自动扩容的,当前容量不足时,会自动根据最佳情况进行扩容(这里就不展开了),但是要知道一点,扩容是重新申请一块内存,也就是说之前的内存没有用上了,看看这个例子
代码:
s1 := make([]int, 1, 1)
s1[0] = 10
s2 := s1
fmt.Printf("%p,%p\n", s1, s2)
fmt.Println(s1, s2)
s2 = append(s1, 10)
fmt.Printf("%p,%p\n", s1, s2)
fmt.Println(s1, s2)
输出:
可以发现,s1数组在s2修改后是没有变化的,因为s2重新申请了一块内存空间,没有用s1的,所以s1不受影响
哪这个不就保证了操作一个切片,另一个切片不受影响吗,跟问题有啥关系,甚至是解决了问题!
但是,这是在扩容的情况下,要是没扩容呢?
没扩容,s2的修改就会影响s1的数据,这就导致了刚才题目的数据发生错乱,原因就是nowNums切片没有扩容,导致已经进入ans的切片数据,在后续的递归中还会发生更改。
怎么解决这个问题,最好的方法是直接取出当前切片的数据,管它后续会不会发生更改
代码:
var ans [][]int
func dfs(nums []int, nowNums []int, l, r int) {
nowNumsCopy := make([]int, len(nowNums))
copy(nowNumsCopy, nowNums) // 直接copy出当前切片数据,不管它后续是否更新
ans = append(ans, nowNumsCopy)
if l == r {
return
}
for i := l; i < r; i++ {
dfs(nums, append(nowNums, nums[i]), i+1, r)
}
}
func subsets(nums []int) [][]int {
ans = [][]int{}
lenNums := len(nums)
dfs(nums, []int{}, 0, lenNums)
return ans
}