I'm having difficulty understanding the Space Complexity of Python List Slices.
For something like
arr[2:] = arr[2:][::-1]
is new space being allocated for the slice (like done in Strings since they are immutable) or is the operation done on the same array?
For something like:
ans = [i+1 for i in range(n)]
for i in range(k):
ans[i:] = ans[i:][::-1]
What will be the space complexity? Will it be different or same than when ans is a string, something like ans = '12345...n'
?
O(n) where n is the length of the slice. Slicing is just "copy part of the list" so time complexity is the same as copy.
Short answer: str slices, in general, copy. That means that your function that does a slice for each of your string's n suffixes is doing O(n2) work. That said, you can avoid copies if you can work with bytes -like objects using memoryview s to get zero-copy views of the original bytes data.
The short answer. Slicing lists does not generate copies of the objects in the list; it just copies the references to them. That is the answer to the question as asked.
copy() or slicing list[:] —is linear to the number of elements in the list. For n list elements, the time complexity is O(n).
Python is strict about adhering to possible side effects. As far as the language is concerned, it is incorrect to perform any of your operations inplace.
Your operation
arr[2:] = arr[2:][::-1]
are three separate slice operations:
arr[2:]
creates a new list from all elements (but the first two) of arr
....[::-1]
creates a new, reversed list from all elements of ...
.arr[2:] = ...
replaces all elements (but the first two) of arr
with ...
.Each slice operation basically amounts to a primitive O(n) copy operation. Since only references are copied, the size or type of elements does not matter.
In your complete example, that amounts to three O(n) slice operations in an O(k) loop:
ans = [i+1 for i in range(n)] # O(n)
for i in range(k): # O(k)
ans[i:] = ans[i:][::-1] # O(n)
In total, the time complexity amounts to O(nk). Space complexity is only O(n), since the temporary slices are reclaimable immediately. You basically end up with the initial list, plus some temporary copies.
Will it be different or same than when
ans
is a string, something likeans = '12345...n'
?
The complexity of the operations does not change - they are still the same primitive operations.
Practically, the CPython implementation cheats with certain objects. For example, +=
is inplace mutation for strings with refcount 1 even though strings are immutable. However, this does not apply to your slice usage.
In general, relying on inbuilt optimisations is a bad idea with Python.
If you are worried about space, start with writing lean code. For example, ans[i:][::-1]
should simply be ans[i::-1]
. That alone halves the temporary space required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With