学习笔记 - Python中列表的可变性质

内有部分力扣题解思想,侵删,仅作个人笔记使用。

参考:力扣题解

今天在写Leetcode78.子集一题时,一开始写了如下题解:

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        track = []

        def dfs(res,track,nums,start):
            res.append(track)
            for i in range(start,len(nums)):
                track.append(nums[i])
                dfs(res,track,nums,i+1)
                track.pop()
        dfs(res,track,nums,0)
        return res

最终输出的是:

[[], [], [], [], [], [], [], []]

后来看了一下力扣里面其他的题解,发现了这个问题,由于Python中的列表是可变的,如果这里直接传入track列表,回溯DFS函数里面在遍历二叉树后会撤销路径track.pop(),很明显最终回溯结束的时候,track列表里面是不含任何元素的,track改变为了空列表,随之res里面添加的列表也会一一变为空列表。

解决方案:

将res.append(track)改为res.append(track[:]),即在传入track列表时浅复制,传入的并不是track列表的指针。

代码如下:

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        track = []

        def dfs(res,track,nums,start):
            res.append(track[:])
            for i in range(start,len(nums)):
                track.append(nums[i])
                dfs(res,track,nums,i+1)
                track.pop()
        dfs(res,track,nums,0)
        return res

另一种解决方法:

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)

        def dfs(start, path):
            res.append(path)
            for i in range(start, n):
            	# python的函数传参属于值传递,即实参各个引用的副本,即别名
            	# 每一层的递归中,由于传递的参数是path + [nums[i]],那么path内存地址会发生变化
            	# 所以每一层的递归函数中,path的内存地址都不同
            	# 所以退回到上一层dfs函数中后,path变量也变为该层dfs函数中的path的内存,所以无需pop即实现了[1,2,3]->[1,2]
                dfs(i + 1, path + [nums[i]])

        dfs(0, [])
        return res

这样的话每一次传入的path其实都是不同的列表(内存地址不相同),也就不需要维护前面的track列表。

Python列表引用与浅复制的区别:

a = [1, 2, 3]
b = a# 别名
c = a[:]# 浅复制
d = list()
d.append(a)# 别名
e = list(a)# 浅复制

a.append(4)

print(b)# [1, 2, 3, 4]
print(c)# [1, 2, 3]
print(d)# [[1, 2, 3, 4]]
print(e)# [1, 2, 3]

a = [1, 2, 3]
b = a# 别名
c = a[:]# 浅复制
d = list()
d.append(a)# 别名
e = list(a)# 浅复制

# 浅复制之后,变量名a指向新的地址,后续append就不会影响b和d了
# b和d扔指向原来的内存区域,且这块内存的引用值会减1,因为a不再指向它了
a = a[:]
a.append(4)

print(b)# [1, 2, 3]
print(c)# [1, 2, 3]
print(d)# [[1, 2, 3]]
print(e)# [1, 2, 3]

你可能感兴趣的:(学习笔记,leetcode,python,力扣)