背景
刷leetcode全排列(题号46)的时候,我写了如下的代码:
class Solution:
def __init__(self):
self.res = []
def traverse(self, nums: List[int], track: List[int], selections: List[int]):
if len(track) == len(nums):
self.res.append(track)
return
for i in nums:
# 进入节点的时候:做选择
if i in track:
continue
track.append(i)
selections.remove(i)
self.traverse(nums, track, selections)
# 离开节点的时候:撤销选择
track.remove(i)
selections.append(i)
return
def permute(self, nums: List[int]) -> List[List[int]]:
# 致命的一行代码
self.traverse(nums, [], nums)
return self.res
结果测试的时候,怎么跑出来都是空。最后粘贴到Pycharm上一步步调试才发现给自己挖了个大坑!
分析
因为我在调用self.traverse时,传入了三个参数,其中第一个和第三个我都是用的nums,在调试时发现当我改第三个变量selections时,第一个变量nums也会跟着变!
原因是列表是可变类型,当我指定第一个参数和第三个参数一模一样的变量时,相当于这俩变量指向了同一个地址。接下来,我不论是对第一个变量进行修改,还是对第三个变量进行修改,这俩地址都没变,改的都是这个地址里的值,所以另一个变量都会跟着变化!
修改
所以我将代码改成了如下,发现结果还是和原来一样的问题!这告诉我,python里的列表这种赋值操作,并没有创建新的对象,而是让两个列表指向了同一个对象!因此,和前面类似的,对列表的更改会反映在两个变量上。
再次修改终于可以了:
注意track也有一样的问题嗷!!
总结
再次敲黑板回顾下可变对象(引用类型)和不可变对象(值类型):
1. 可变对象:对一个变量进行操作时,其值是可变的,但是对值的修改并不会引起新的对象,即地址是不会变的,只是地址中的内容发生了变化或者地址得到了扩充!
2. 不可变对象:可以理解为一个萝卜一个坑,地址中的值是不会变的,要变就变地址!
怎么记呢?可变对象和不可变对象所说的「可变」「不可变」都是针对值而言的。值可变,说明地址不变,则是引用类型!同理,不可变对象说明值不能动,那就是值类型了。