C++ Problem-Solving Power Tool - STL (Standard Template Library) | LeetCode
🌐 中文版:繁體中文
What is the STL (Standard Template Library)?
In C++, the STL is a collection of containers. Different containers implement different data structures, and they’re actually implemented with heavy use of C++ templates.
Algorithms are implemented on top of these data structures, and the STL also provides operations on containers, with different data-structure containers having different ways of operating on them.
A template, simply put, is a predefined structure that can be used with different data types. For example, I write an array template, but it can be int[], float[], double[], or char[].
template <typename T>is the usual way to define a template. Think of the stuff inside the angle brackets as the compiler doing copy-paste for you.
STL components
There are 6 main components:
- Container
- Algorithm
- Iterator
- Function object (functor)
- Adaptor
- Allocator
For problem solving, the key things to focus on are containers and iterators, plus algorithms like
sort, which usually exist as functions and are mostly one-off computations. But on LeetCode you mostly have to implement things yourself, so I won’t go into detail on those.
STL containers
Containers are usually the data-structure part, existing as variables, and can continuously interact with and maintain data.

The container types usable on LeetCode are: Vector, List, Stack, Queue, PriorityQueue (Binary Heap), Set/MultiSet, Unordered Set, Map/MultiMap, Unordered Map
STL iterators
An iterator is used to visit all elements of a container in order, and also to refer to a specific element in a container, which can express a search result or the endpoint of a range.
Below is an example
1 |
|
From: https://hackmd.io/@sa072686/cp/%2F%40sa072686%2FS11uDpiuH
You can see that inside the for loop we can use vector<int>::iterator to declare an iterator it, which starts from the beginning of the vector and increments by 1 while not equal to the end.
A few key points to observe here:
- An iterator is declared as
container_type::iterator - Iterators support
++or--operations - Containers that support iterators can basically all use
.begin()to get the first element and.end()to get the position one past the last element, which is a nonexistent empty slot, matching the STL’s left-inclusive, right-exclusive rule - Using the dereference operator
*you can get the value at the iterator’s current address
Most of the time, understanding it as a pointer is fine (a pointer is a special case of an iterator; it’s also an iterator).
auto type deduction
From the example above you’ll notice one thing: declaring an iterator is really verbose, so there’s a lazy trick, using auto to let the compiler do automatic type deduction. The compiler decides the variable’s type based on the type of the initial value.
You can rewrite the for loop like this
1 | vector<int> v; |
For vector, this is a random-access iterator. What is random access?
Random Access
If a container can be accessed in $O(1)$ complexity, then it has the random access property.
For problem solving, the iterators you’ll use differ by container type. See the table below.
| Iterator Type | Description | Container |
|---|---|---|
| Bidirectional iterator | Read and Writes forward and backward | list,set,multiset,map,multimap |
| Random access iterator | Read and Write with random access | vector,deque,array,string |
Common STL containers
Vector
Think of it as a dynamic array
- You can access and modify elements in $O(1)$ time
- Modifying an element in the middle of the collection is $O(n)$. It’s generally recommended to swap the value to be deleted to the end first, then delete it.
1 |
Vector initialization
1 | // declare an int vector |
Common Vector operations
1 | v.empty(); |
For the last v.erase(), if you don’t care about order you can use
1 | swap(v[n], v.back()); // swap position n with the last position |
There are some more operations
1 | // Traversal |
Traversal can also be written in the form below, but this involves iterators, which are covered later.
1 | for(auto it = v.begin(); it != v.end(); ++it) { |
List
Operations like moving or deleting are all $O(1)$
1 |
1 | // delcare list container |
Common operations:
1 | l.empty(); |
Moving a value from one list into another
1 | l.splice(iterator pos, list& x); // splice all of x's elements into l at position pos. |
Stack
Property: LIFO (Last-In-First-Out). A Stack is a finite sequence of objects, and the items that can be deleted, retrieved, and modified in the sequence are only the most recently inserted obj.
1 |
|
Queue
Property: FIFO (First-In-First-Out), so insertion can only happen at the tail, and deletion, retrieval, and modification are only allowed from the head.
1 |
|
PriorityQueue
It’s actually a heap structure, often used in Dijkstra’s algorithm
1 |
|
Note that PQ’s pop and push complexity is $O(\log n)$
make heap operations
There’s a space-saving method: use make_heap to turn a vector into a heap, which makes the space complexity $O(1)$
1 | vector<int> nums{1, 3, 5, 2, 4, 6}; |
Set and MultiSet
A Set is a set
By default a set sorts from small to large, and the elements in a set container are unique, with no duplicates.
A multiset allows duplicate elements. unordered_set is an unsorted set. unordered_multiset is an unsorted multiset.
1 |
|
Map and MultiMap
A Map is like a lookup table, using key-value pairs to implement a one-to-one mapping
- By default it also sorts from small to large
- multimap allows multiple identical key-value pairs.
- unordered_map is an unsorted map.
- unordered_multimap is an unsorted multimap.
Simple example:
1 |
|
1 |
|
Map’s underlying implementation uses a red-black tree, so its mappings are sorted by key, which is why insertion, lookup, and deletion are all $O(\log n)$.
unordered_map’s underlying implementation uses a hash table and is unordered, so the complexity is $O(1)$.
When using unordered_map, you look up the value by the hash produced from the key. Since it’s a hash, there’s a collision problem.
As we know a Bucket is a slot in the container’s internal hash table to which all the element are assigned based on the hash value of their key . Buckets are numbered from 0 to bucket_count.
If the number added exceeds bucket_count, the map automatically enlarges the bucket slots and recomputes the hash of all items.
When the Load Factor(load_factor) reaches a certain threshold, the container increases the number of buckets and rehashes the map.
Using the code below enlarges the buckets to n and recomputes the hash table.
1 | m.refresh(n); |
If we know the size in advance, we can use the following function to reserve buckets up to n directly, avoiding exceeding the threshold and needing to enlarge the container. Reserving n buckets in advance also helps avoid hash collisions.
1 | m.reserve(n); |
Reference
[1] https://hackmd.io/@sa072686/cp/%2F%40sa072686%2FS11uDpiuH
[2] https://jasonblog.github.io/note/c++/stl_rong_qi_4e0029_-_ji_ben_jie_shao.html
[3] https://hackmd.io/@meyr543/BkgMaiV6Y#Vector
[4] https://blog.csdn.net/weixin_42292229/article/details/125523668
[5] https://ikaminyou.medium.com/leetcode-%E5%88%B71500%E9%A1%8C%E5%BF%83%E8%B7%AF%E6%AD%B7%E7%A8%8B-8614284f03da






