Python's list implementation uses a dynamically resized C array under the hood, removing elements usually requires you to move elements following after up to prevent gaps.
list.pop() with no arguments removes the last element. Accessing that element can be done in constant time. There are no elements following so nothing needs to be shifted.
list.pop(0) removes the first element. All remaining elements have to be shifted up one step, so that takes O(n) linear time.
Python's list implementation uses a dynamically resized C array under the hood, removing elements usually requires you to move elements following after up to prevent gaps.
list.pop() with no arguments removes the last element. Accessing that element can be done in constant time. There are no elements following so nothing needs to be shifted.
list.pop(0) removes the first element. All remaining elements have to be shifted up one step, so that takes O(n) linear time.
To add to Martijn's answer, if you want a datastructure that has constant time pops at both ends, look at collections.deque.
Does pop(i) have a Time Complexity of O(n) or O(k)?
Is popleft() faster than pop(0) ?
Yes. list.pop(0) is O(n), and deque.popleft() is O(1).
python - What are effects on overhead of using list.pop(0)? - Stack Overflow
I was surprised at how slow list.pop() is! And list.remove() is even many times slower
Yes, it is O(1) to pop the last element of a Python list, and O(N) to pop an arbitrary element (since the whole rest of the list has to be shifted).
Here's a great article on how Python lists are stored and manipulated: An Introduction to Python Lists.
Pop() for the last element ought to be O(1) since you only need to return the element referred to by the last element in the array and update the index of the last element. I would expect pop() for an arbitrary element to be O(N) and require on average N/2 operations since you would need to move any elements beyond the element you are removing one position up in the array of pointers.
If we have a deque as : queue=collections.deque([0]) and list as : arr=[0].
Will queue.popleft() be faster ? Will arr.pop(0) be slower? Is there a difference in time complexity?
Yes. list.pop(0) is O(n), and deque.popleft() is O(1).
popleft is just a shortcut to pop(0), same way pop() is a shortcut to pop(len(sequence)-1), it's not suddenly performing a different operation with a different time complexity, as is also mentioned in the documentation
Indexed access is O(1) at both ends but slows to O(n) in the middle. For fast random access, use lists instead.
If you look into the actual pop code in cpython/Objects/listobject.c, you'll see there are memcpy/memmove calls for the case where you're not popping the last element (it's actually done in the call to list_ass_slice).
Hence, it's not a non-trivial expense (certainly not O(1) as suggested in a comment - that may be true for a linked-list type structure but that's not what Python lists are). It's the fact that it's doing the element removal in-place that means that the id won't change but that doesn't mean it's efficient.
For example, consider the list:
0 1 2 3 4 5 <- index
+-----+-----+-----+-----+-----+-----+
| A | B | C | D | E | F |
+-----+-----+-----+-----+-----+-----+
Popping the last element is usually an O(1) operation since it simply needs to take out F and reduce the size.
However, popping the first element means taking out A and then moving all the elements B..F to the position where A was:
0 1 2 3 4 <- index
+-----+-----+-----+-----+-----+
| B | C | D | E | F |
+-----+-----+-----+-----+-----+
But keep in mind it probably won't matter unless your lists get really big. The objects themselves aren't being reconstructed since the list only holds references to them.
The list has a buffer that holds references to its values. After pop(0), the remaining references need to be copied down one in this buffer. If you happen to pass certain thresholds a smaller buffer will be allocated and the references will be copied there.
Unless the list is large, it doesn't make much of a difference. If the list is large and you do a lot of pop(0), perhaps reversing it and doing pop() from the end would make sense.
I know there is a list.clear(), I'm just sharing that I didn't expect that using list.pop() and list.remove() specifically could slow down the program that much.
li = list(range(500000))
Creating a list is quick.
So we are going to test out pop/remove specific values. For the purpose of this "benchmark", we are going to remove all elements from the list:
while (li):
li.pop(0)
It took 74.735 seconds to pop all the elements! It's ridiculously long.
I KNOW it would have been much faster if I even had used li.pop() without the index or maybe used filter function, list comprehension with conditional or whatever
But that's what I'm trying to show, how slow it is to remove certain list items specifically using pop and remove methods.
And li.remove(), which always requires a specified value to remove, is even worse than pop!
for num in li:
li.remove(num)This one took me 303.268 seconds to complete. How crazy it is.
I've been having fun with abstract data structures. Implemented linked lists and a queues running on linked lists.
And for the sake of interest, I decided to compare the performance of the queue based on the linked list and the usual python list. And I was surprised. When my linked list Queue dequeued 500.000 elements in 0.5 seconds, while python list Queue was doing it in 75 seconds.