python - 在 python 列表中交换元素的时间复杂度

标签 python list time-complexity

如果我执行以下操作,在 python 列表中交换元素的时间复杂度是多少

案例 1:(惯用方式)交换列表中的两个元素

>>> a = [1, 2, 3, 4, 5]
>>> a[1], a[3] = a[3], a[1]  # Pythonic Swap --> x, y = y, x

>>> print(a)
[1, 4, 3, 2, 5]

问题1:交换步骤的时间复杂度是多少? python 内部是做什么的。


案例 2:(非常低效的方式)交换列表中的两个元素

>>> a = [1, 2, 3, 4, 5]
>>> temp1, temp2 = a[1], a[3]
>>> del a[1]  # a = [1, 3, 4, 5]
>>> a.insert(1, temp2)  # a = [1, 4, 3, 4, 5]
>>> del a[3]  # a = [1, 4, 3, 5]
>>> a.insert(3, temp1)  # a = [1, 4, 3, 2, 5]
>>> print(a)
[1, 4, 3, 2, 5]

如果我这样做,每当我在任何索引处插入或删除时,该索引右侧的所有内存地址都需要分别向右或向左移动/复制一步。所以它需要 O(K),其中 K 是我们插入或删除的索引右侧的地址数。如果我错了,请纠正我。


一些分析

如果列表非常小,那么运行时复杂度与我使用的任何方法(Case1 或 Case2)无关紧要。但是,如果列表非常大,如 a = [i for i in range(0,1000000)] 那么交换列表中两个元素的有效方法是什么?在这里,我使用百万记录列表交换索引 100 和 54321 对上述两种情况进行了一些基本分析,结果如下。令人惊讶的是,这两种情况的性能几乎相同。

a = [i for i in range(0,1000000)]

Case1 分析

$时间python3 case1_script.py

real    0m0.129s
user    0m0.088s
sys     0m0.041s

$ python3 -m cProfile case1_script.py

3 function calls in 0.060 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.047    0.047    0.060    0.060 list_ele_swap_profile.py:1(<module>)
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    1    0.013    0.013    0.013    0.013 {range}

Case2 分析

$时间python3 case2_script.py

real    0m0.132s
user    0m0.090s
sys     0m0.042s

$ python3 -m cProfile case2_script.py

5 function calls in 0.115 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.048    0.048    0.115    0.115 list_ele_swap_profile.py:1(<module>)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     2    0.001    0.001    0.001    0.001 {method 'insert' of 'list' objects}
     1    0.066    0.066    0.066    0.066 {range}

问题 2:如果列表非常大,如上所示,交换列表中两个元素的有效方法是什么。

问题 3: 在思考这个问题时我还有一个疑问,那么如果从列表需要复制/移动内存地址,那么如何从列表中删除一个元素是 O(1)。


PS:我不认为这是一个重复的问题,我已经完成了以下问题,但没有一个是我要找的。我有兴趣了解上述操作的时间/空间复杂度。谢谢。

最佳答案

答案取决于您对 python 的实现。 我假设您对默认的 CPython 实现感兴趣。 从现在开始,我说“python”而不是“cpython”。

Python 中的列表或多或少像 C 中的数组一样适合您的目的。

删除其中的一个元素需要移动它上面的所有元素,插入也是如此,因此这两个操作是 O(i),其中 i 是您删除/插入的项目之后的项目数。 您的第一个答案是 O(1),这样更好。

你的分析是错误的,因为这个数组的大小实际上很小,而且你的时间安排也考虑了构建数组的时间。 尝试以下实验:构建一次大小为 100000 的数组,并尝试交换其中的两个元素 100000 次。 您会看到第一个代码比第二个代码至少快 100 倍(这是我在计算机上获得的数字),这当然是交换 python 列表中元素的好方法。

关于python - 在 python 列表中交换元素的时间复杂度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30745678/

相关文章:

python - Pandas DataFrame 获取索引符合特定条件的行

python - 单独的文件或一个文件用于类/方法

python - 比较当前元组列表和下一个元组列表的值

algorithm - 如果基本情况不是在恒定运行时而是在多项式运行时运行,那么主定理是否适用?

c# - Dictionary 的时间和空间复杂度是多少?

python - 无法导入 nvprof 生成的配置文件数据

python - SQLAlchemy create_engine 如何导入 Engine 类?

list - 如何使用迭代生成给定长度的列表元素的所有组合?

c# - 将字符串拆分为基于单词长度的列表 C#

performance - 巴比伦方法的时间复杂度