1

What is the complexity of iterating through an unordered_map implemented using hashing with chaining? Specifically does this involve traversing all the buckets, in which case complexity is O(Buckets + NElements) vs. ideal, e.g. O(NElements)

user3882729
  • 1,339
  • 8
  • 11
  • From [iterator.requirements.general](https://timsong-cpp.github.io/cppwp/n4861/iterator.requirements.general#13), "All the categories of iterators require only those functions that are realizable for a given category in constant time (amortized). Therefore, requirement tables and concept definitions for the iterators do not specify complexity." So the increment is O(1), and going thru the whole container is O(N). – 1201ProgramAlarm Jun 30 '21 at 19:11
  • @1201ProgramAlarm: That requirement seems incompatible with iterator adapters (and filters in the ranges proposal, etc). – Ben Voigt Jun 30 '21 at 20:25

1 Answers1

1

What is the complexity of iterating through an unordered_map?

O(N) where N is the number of elements stored. The most relevant part of the Standard:

All the categories of iterators require only those functions that are realizable for a given category in constant time (amortized). Therefore, requirement tables for the iterators do not have a complexity column.

You can see more about that - and discussion - in e.g. this answer

..implemented using hashing with chaining?

All unordered maps use hashing with chaining - see here.

Specifically does this involve traversing all the buckets, in which case complexity is O(Buckets + NElements) vs. ideal, e.g. O(NElements)

It does what you're calling "ideal". In the case of GCC, buckets hold iterators into a singly linked list, and it's that usually-shorter list is traversed when iterating over the container. Usually because even without changing the default max_load_factor() of 1.0 it's possible for the number of buckets to exactly equal the number of elements - any more elements than that will then trigger a resize. But, big-O efficiency of iteration becomes more important when almost all the elements have been deleted, as the number of buckets won't be automatically resized downwards (i.e. reduced).

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • Thanks Tony, with the singly linked list, presumably the nodes there have a link back to the buckets they belong to in order to handle deletion? – user3882729 Jul 06 '21 at 03:57
  • 1
    @user3882729: great question. Actually, GCC's unordered container iterators only have a pointer to the node, and the node has only a pointer to the next-node and storage, but in the "storage" there's both a value object and the hash code, so it can divide that pre-calculated hash value by the bucket count to find the bucket. You can see this easily in gdb: `(gdb) print *it._M_cur $2 = {[...]{ = {_M_nxt = 0x41a330}, _M_storage = [...] {__data = "\006\000\000", __align = {}}}}, _M_hash_code = 6}` – Tony Delroy Jul 14 '21 at 16:27