python中对列表的复制操作用的比较广泛,之前写过一篇博客Python:复制列表,禁止函数修改列表(副本,切片表示法[:])详细讲述了python中的 [:] 操作,但是 [:] 操作仅仅只是浅拷贝,具有很大的局限性。
一:[:] 浅拷贝操作
用法(path和num都是数组):
path = num[:]
如 path = num[:] 中,
path只会创建num的浅层副本,即只复制num中的元素的引用,而不会复制嵌套对象本身。
什么意思呢?简单举例来说:
如果num是一维数组的话,浅拷贝与深拷贝一样,对数组path怎样操作都影响不到数组num。
path修改元素(num值不变):
如果num是二维或二维以上的数组的话,path只会复制num数组的表面,如果修改path中的元素,原始列表num中的对应元素也会被修改。但是,如果你对path整体进行修改(例如path.append([3,3])),则不会影响原始列表num。
path修改元素(num也会改变):
path增加元素(num值不变):
所以 [:] 浅拷贝的缺陷很明显,一旦拷贝嵌套列表(二维及二维以上),对复制的列表进行修改操作会影响原列表,无法恢复。
二:copy()浅拷贝操作:
这里主要扩充一下copy()函数的知识点,copy()函数的用法是:path = num.copy()。
path = num.copy()
path = num.copy() 与 path = num[:] 操作一样
这种方式也是对列表进行浅拷贝操作。它会创建一个新的列表path,但是该列表中的元素依然是原始列表num中元素的引用。因此,和path = num[:]一样,对path中的元素的修改会影响原始列表num中的对应元素,但对path整体的修改不会影响原始列表num。
三:标准库函数copy.deepcopy() 深拷贝(蓝桥杯可用)
用法:
import copy
path = copy.deepcopy(num)
这种方式是对列表进行深拷贝操作。它会递归地复制原始列表num及其内部的所有对象,包括嵌套的列表、字典等。这样,创建的新列表path与原始列表num完全独立,对其中一个对象的修改不会影响另一个对象。深拷贝操作确保了复制后的对象与原始对象完全独立,是最安全的复制方式。
如图所示:
总结:
在 Python 中,如果a是一个数组,copy.deepcopy(a)
和 a.copy()
都是用于复制对象的方法,但它们的行为和时间复杂度是有差异的。具体差异和时间复杂度的分析如下:
1. b = a.copy()
:
a.copy()
是 浅拷贝 方法。它创建了一个新的对象,并将原对象的引用传递给这个新对象。这意味着,对于嵌套的可变对象(例如列表中的列表),浅拷贝仅仅复制了这些对象的引用,而没有复制嵌套对象本身。
时间复杂度:
-
对于 可变对象(例如列表、字典等):
- 如果
a
是一个 列表,那么a.copy()
只会创建一个新的列表对象,但列表中的元素(如果是可变对象)仍然指向原来的对象。因此,对于列表中的每个元素,都是复制其引用,没有递归复制。 - 这个操作的时间复杂度是 O(n),其中 n 是
a
中元素的数量,因为只需要复制一层结构。
示例:
a = [1, 2, 3] b = a.copy() # 创建一个浅拷贝
- 如果
-
对于 字典,也是类似的,
a.copy()
只会复制字典的键值对的引用,而不会递归复制键值对中的对象。- 时间复杂度同样是 O(n),其中 n 是字典中的元素数量。
总结:
a.copy()
的时间复杂度是 O(n),其中 n 是对象中的元素数量。- 它仅创建了一层新结构,而没有递归地复制嵌套对象。
2. b = copy.deepcopy(a)
:
copy.deepcopy(a)
是 深拷贝 方法。它会递归地复制 a
中的所有对象,包括嵌套的对象,直到没有更深层的对象为止。这意味着深拷贝会完全复制整个对象及其嵌套的所有内容,而不再引用原始对象。
时间复杂度:
- 深拷贝会递归地遍历整个对象的结构(包括所有嵌套的对象)。对于每一个 可变对象(如列表、字典、集合等),都会创建一个新的对象,并递归地复制其中的元素。
- 假设
a
是一个包含嵌套结构的对象(如列表中的列表、字典中的字典等),则深拷贝的时间复杂度通常取决于 对象的嵌套深度和元素数量。 - 如果对象是树形结构,深拷贝的时间复杂度是 O(m),其中 m 是对象中的所有元素(包括嵌套对象中的元素)。但是深拷贝需要访问并复制每一个元素,因此它的时间复杂度是 O(m),而 m 是对象中所有元素的总数(包括嵌套结构的元素)。
举个例子:
import copy
a = [[1, 2], [3, 4], [5, 6]]
b = copy.deepcopy(a) # 递归复制整个嵌套列表
在这个例子中,copy.deepcopy
会递归地复制每个嵌套列表。
总结:
copy.deepcopy(a)
的时间复杂度是 O(m),其中 m 是对象中所有元素的总数量,包括嵌套对象的元素。- 它递归地复制整个对象的所有内容,确保所有对象都是新创建的副本,不会与原始对象共享引用。
总结对比:
方法 | 时间复杂度 | 说明 |
---|---|---|
a.copy() | O(n) | 仅复制对象的第一层结构,嵌套的对象仍然引用原对象,因此适用于不含深层嵌套的对象或不需要复制嵌套对象的场景。 |
copy.deepcopy(a) | O(m) | 递归地复制对象及其嵌套的所有内容,适用于需要完全独立副本的情况,其中 m 是所有元素的总数量。 |
- 浅拷贝 (
a.copy()
) 适用于结构比较简单或不需要修改嵌套结构的情况,时间复杂度较低。 - 深拷贝 (
copy.deepcopy(a)
) 适用于需要完全独立副本,且包含嵌套结构的情况,但时间复杂度较高,因为需要递归地复制每一层的内容。