Recommended resources
The freely available resources this lesson will direct you to.
Hello! Last lesson, you counted the work visible in C++ loops and included hidden costs such as copying a vector. Now the hidden work is inside containers: the same-looking insert or find call can have a different cost depending on how the container stores its elements.
This lesson gives you an interview-ready account of common STL operations. Unless stated otherwise, let be the number of elements currently in a container, and assume inexpensive elements and keys such as integers. We’ll distinguish a guaranteed cost from an average-case cost and an amortized cost.
Sequences: where does the new element go?
A vector keeps its elements in contiguous storage. That makes v[i] constant-time: once the starting location is known, an index identifies the element directly. But inserting near the front usually requires shifting the elements that follow it.

Read the storage discussion and the short complexity list in cppreference’s std::vector reference. Focus on the distinction between end insertion and insertion elsewhere—not just the Big-O labels.
std::vector - cppreference.com
cppreference’s vector overview connects capacity and reallocation to the costs of common operations.
On the std::vector page, find the overview paragraph beginning “The storage of the vector is handled automatically.” Read the storage discussion and complexity list. Continue through the final bullet about insertion or removal; notice that capacity can exceed size.
Suppose v has integers. v.push_back(7) is amortized , whereas v.insert(v.begin(), 7) is because it must make room before the existing elements. Erasing at the front similarly shifts the survivors; pop_back() does not. Looking for a value in an unsorted vector is a different task from accessing an index: std::find(v.begin(), v.end(), 7) may inspect all elements, so it is .
Why qualify push_back with amortized? A vector may have unused capacity, allowing an append without moving its existing elements. When capacity runs out, an append may need a larger allocation and move or copy all elements, making that one call . Across many appends, those occasional costly calls are spread over the inexpensive ones; appending inexpensive elements one at a time takes total time. Amortized is a guarantee about a sequence of operations, not a claim that every call is constant-time. It is also different from a hash table’s average-case , which depends on its hashing behavior.

std::string has a similar contiguous-storage trade-off for characters: character indexing is , appending one character is amortized , and inserting or erasing near the front is . Size matters for operations involving another string: appending characters costs amortized, and making a substring of length copies characters, costing .
Two other sequence containers are useful contrasts. A std::deque supports indexed access and efficient insertion and removal at both ends—typically stated as constant time for individual end operations, with occasional storage-management costs. Inserting in its middle is . A std::list supports insertion or erasure when you already have an iterator to the position, but finding that position takes ; it has no list[i]. This distinction matters when an interview question says “insert at a known position”: known index and iterator already in hand are not the same condition.
Containers that restrict what you can access
A std::stack exposes its newest element through top(); a std::queue exposes its oldest element through front(). Neither gives you arbitrary indexing. Their usual push, pop, and exposed-element access take time with their default underlying containers. A std::priority_queue instead keeps its highest-priority element available at top(): top() is , while push() and pop() are .
Watch the stack, queue, and priority-queue portions of take U forward’s Complete C++ STL in 1 Video. Concentrate on which element each container lets you remove, then attach the cost to that operation.
Complete C++ STL in 1 Video | Time Complexity and Notes
take U forward demonstrates the interfaces of three common container adaptors and states their principal operation costs.
Watch stack and queue for top versus front and their constant-time operations. Then watch priority queue for its largest-element-first default and the difference between top and push or pop. Treat “all operations” as shorthand for the principal operations shown, not for work such as copying an entire container.
A priority queue uses a heap-like arrangement rather than keeping every element fully sorted. It can reveal the top immediately, but adding an element or restoring the arrangement after removing the top may require work proportional to the heap’s height, . Heap mechanics will return later in the course; the interface and costs are enough for now.
Searching by key: ordered trees versus hash tables
A vector index is a position. A std::set instead answers whether a key is present; a std::map associates each unique key with a value. Both maintain keys in sorted order. Their commonly used find, insert, and erase by key operations take , including in the worst case, assuming comparisons are constant-time. Their size() and empty() calls are , not logarithmic.
Watch take U forward’s set and unordered-set segments for the contrast between sorted keys and unsorted keys. The important correction to keep in mind is that “unordered” means no promised iteration order, not that the container randomly shuffles its elements.
Complete C++ STL in 1 Video | Time Complexity and Notes
These portions of take U forward’s STL video introduce unique-key sets and contrast ordered lookups with typical hash-table lookups.
Watch ordered set for sorted iteration and find, insert, and erase. Skip the intervening multiset discussion, then watch unordered set for average versus worst-case costs. Interpret the video’s broad complexity statements as applying to key-based operations; size() and empty() remain constant-time.
Maps make the same structural comparison but store key–value pairs. Watch the opening of The Cherno’s Maps in C++ (std::map and std::unordered_map) to see why a lookup by key differs from looking up a vector element by position.
Maps in C++ (std::map and std::unordered_map)
The Cherno introduces maps as key–value containers and shows the broad structural difference between ordered maps and hash-based unordered maps.
Watch map foundations, particularly the discussion beginning with the two map varieties around 00:02:12. Focus on sorted keys in std::map versus bucket-based lookup in std::unordered_map. A sorted vector need not be scanned linearly for every search if you use binary search; the comparison here is about the containers’ interfaces and storage.
std::unordered_set and std::unordered_map use hashing. For their key-based find, insert, and erase, state average time, worst-case time. A useful hash distribution generally lets an operation examine little beyond the relevant bucket. If many keys collide, it may have to examine many elements. An insertion can also occasionally trigger a rehash of existing elements. Neither unordered container promises sorted—or random—iteration order.
Here is the compact comparison to keep at hand:
| Container | Access or lookup | Common insertion and removal |
|---|---|---|
vector, string |
Index ; unsorted value search | End append amortized ; middle |
deque |
Index | Ends in the usual operation-cost summary; middle |
list |
Search ; no indexing | at an already-known iterator |
set, map |
Find by key | Insert or erase by key |
unordered_set, unordered_map |
Find by key average , worst | Insert or erase by key average , worst |
stack, queue |
Exposed element | Usual push and pop |
priority_queue |
top() |
push() and pop() |
The table concerns individual operations, except where it explicitly says amortized. Traversing all elements of any of these containers is , even if one lookup is .
Turn container costs into a function cost
Last lesson’s loop-counting method still applies: multiply the number of calls by the relevant operation cost. For example:
std::unordered_map<int, int> frequencies;
for (int value : values) {
++frequencies[value];
}
If values has integers, the loop makes hash-table accesses. operator[] finds a key or inserts a new entry with a zero-initialized integer value before the increment. The function fragment is average time and uses additional storage if every value is distinct. With severe collisions, its time can reach . If frequencies were a std::map<int, int>, the same loop would instead take time, with a worst-case logarithmic cost per access.
One final qualification makes your answers more precise: the table assumed cheap integer operations. Hashing a string key must examine its characters, and comparing two strings may examine multiple characters. When keys or elements have variable size, include that work rather than calling every comparison, hash, copy, or destruction .
Takeaways
For an interview, state the operation and its condition: vector append is amortized , list insertion is with a position iterator already available, and unordered-container lookup is on average but in the worst case. Ordered sets and maps offer key operations and sorted iteration; priority queues offer an top but updates.
Next, you’ll use these costs while turning an interview prompt into explicit inputs, outputs, constraints, and assumptions. Those details often determine which container’s guarantees actually matter.