15

I see a lot of questions about the run-time complexity of python's built in methods, and there are a lot of answers for a lot of the methods (e.g. https://wiki.python.org/moin/TimeComplexity , https://www.ics.uci.edu/~pattis/ICS-33/lectures/complexitypython.txt , Cost of len() function , etc.)

What I don't see anything that addresses enumerate. I know it returns at least one new array (the indexes) but how long does it take to generate that and is the other array just the original array?

In other words, I'm assuming it's O(n) for creating a new array (iteration) and O(1) for the reuse of the original array...O(n) in total (I think). Is the another O(n) for the copy making it O(n^2), or something else...?

Community
  • 1
  • 1
10'004
  • 163
  • 1
  • 1
  • 10
  • 2
    O(n^2) wolud mean O(n * n) not O(n + n) – Ryan Haining Jun 30 '15 at 00:21
  • 2
    `enumerate` returns an enumeration object — which is an iterator object — not a list, so it's complexity is going to depend somewhat on how it's used. – martineau Jun 30 '15 at 00:24
  • there is no `list.enumerate`. There is [built-in `enumerate()` function](https://docs.python.org/3/library/functions.html#enumerate) that works with arbitrary iterables. – jfs Jul 01 '15 at 01:22

3 Answers3

30

The enumerate-function returns an iterator. The concept of an iterator is described here.

Basically this means that the iterator gets initialized pointing to the first item of the list and then returning the next element of the list every time its next() method gets called.

So the complexity should be:

Initialization: O(1)

Returning the next element: O(1)

Returning all elements: n * O(1)

Please note that enumerate does NOT create a new data structure (list of tuples or something like that)! It is just iterating over the existing list, keeping the element index in mind.

You can try this out by yourself:

# First, create a list containing a lot of entries:
# (O(n) - executing this line should take some noticeable time)
a = [str(i) for i in range(10000000)] # a = ["0", "1", ..., "9999999"]

# Then call the enumeration function for a.
# (O(1) - executes very fast because that's just the initialization of the iterator.)
b = enumeration(a)

# use the iterator
# (O(n) - retrieving the next element is O(1) and there are n elements in the list.)
for i in b:
    pass  # do nothing
Felix
  • 6,131
  • 4
  • 24
  • 44
11

Assuming the naïve approach (enumerate duplicates the array, then iterates over it), you have O(n) time for duplicating the array, then O(n) time for iterating over it. If that was just n instead of O(n), you would have 2 * n time total, but that's not how O(n) works; all you know is that the amount of time it takes will be some multiple of n. That's (basically) what O(n) means anyway, so in any case, the enumerate function is O(n) time total.

Sam Estep
  • 12,974
  • 2
  • 37
  • 75
  • 1
    As in the example @caenyon has and given how people often use enumerate, I'm assuming a situation like `for i,v in enumerate(a)` would be O(n+n+1) and thus O(2n)? – 10'004 Jun 30 '15 at 19:42
  • 3
    @10'004 No. In any situation where the dominant term in the complexity of an algorithm is linear (i.e. if the time taken by an algorithm is some multiple of *n*), the complexity is O(n). Whether the actual time is 2n, 3n, 4n, 0.5n, or any multiple of *n*, the complexity is always O(n). – Sam Estep Jun 30 '15 at 20:07
  • 1
    @10'004: `enumerate()` may work with infinite generators and therefore it does not copy its input. Though it won't change the time complexity even if it did (for a finite input). – jfs Jul 01 '15 at 01:21
5

As martineau pointed out, enumerate() does not make a copy of the array. Instead it returns an object which you use to iterate over the array. The call to enumerate() itself is O(1).

James
  • 1,198
  • 8
  • 14