2021-03-31

"本文探讨了在Go语言中使用回溯算法时遇到的问题,特别是涉及到切片操作导致的意外结果。文章解释了Go的传值特性以及切片的数据结构,指出切片在传递过程中虽然拷贝了描述信息,但底层数组共享,从而导致对切片的修改会影响到其他引用该底层数组的切片。为避免这种问题,文章建议使用`append([]int{}
摘要由CSDN通过智能技术生成

Go踩的坑

今天在用go做一道回溯算法题

func subsetsWithDup(nums []int) [][]int {
	sort.Ints(nums)

	var res [][]int = make([][]int, 0)

	path := make([]int, 0)

	var dfs func(bool, int)

	dfs = func(preNum bool, pos int) {
		if pos == len(nums) {
			//fmt.Println(path)
			//fmt.Println(res)
			res = append(res, path)
			//fmt.Println(res)
			//fmt.Println("--------------------------------------------------")
			return
		}

		dfs(false, pos+1) //不加入当前的数字

		if preNum == false && pos > 0 && nums[pos-1] == nums[pos] { //如果前面没放,当前数字与前面相同,那么不用经过当前放的分支,因为和前面放当前不放相同
			return
		}

		path = append(path, nums[pos])
		dfs(true, pos+1) //加入当前位置的数
		path = path[:len(path)-1]

	}
	dfs(false, 0)
	return res
}

发现在回溯改变path切片时,res中上次放入的path结果也会改变,这与Go的函数参数传递方式有关。

Go的传值

在Go中,所有的传参都是传值,也就是都是拷贝一份传递的参数,那么为什么已经拷贝传递进res中的path会改变呢?这与Go的切片数据结构有关。

基础类型

Go的基本类型:byteintboolstring均是传值引用

数组

Go的数组也是传值引用

func main() {
	var array = [3]int{0, 1, 2}
	var array2 = array
	slice2[2] = 5
	fmt.Println(array, array2)
}

// 输出结果:
// [0 1 2] [0 1 5]

也可以使用指针来实现传引用:

func main() {
	var array = [3]int{0, 1, 2}
	var array2 = &array
	array2[2] = 5
	fmt.Println(array, *array2)
}
// 输出结果:
// [0 1 5] [0 1 5]

切片

先要对切片数据结构有了解

示意图:
在这里插入图片描述
一个切片是一个数组片段的描述。它包含了指向数组的指针,片段的长度, 和容量(片段的最大长度)。

下面是slice在源码中的结构

// runtime/slice.go
type slice struct {
	array unsafe.Pointer // 数组指针
	len   int // 长度 
	cap   int // 容量
}

在这里插入图片描述
虽然切片传递还是传值,会拷贝一份切片,但底层数组不会拷贝,拷贝出的新切片与老切片指向同一个底层数组,自然修改老切片会影响新切片中的值。
所以对于切片来说,传值差不多就是传引用了。要想真正的拷贝一份可以这样:

res = append(res, append([]int{},path...))

在这下面这句话中

 append([]int{},path...)

[]int{}是新建一个空切片,也可以[]int(nil),因为要用的时append的返回值,不需要给这个新的空切片起名
那么新建的空切片底层数组是空,与老切片不公用一个底层数组,向其中添加元素时会进行扩容
...是一种语法糖,在slice中用法是将path打散再一个个放入新切片中
append返回一个包含原切片所有元素加上新添加元素的切片。
此时这个切片就可以添加到res中而不受path影响了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值