如果我按照以下方式交换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 Profiling
$ time 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 Profiling
$ time 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:我不认为这是一个重复的问题,我已经阅读了以下问题,但它们都没有我想要的。我想知道上述操作的时间/空间复杂度。谢谢。