STL is a combination of algorithms and containers. Algorithms access containers through iterators.
Containers
Sequence containers - emplemented as array or linked list.
1.Vector is a dynamically allocated contiguous array in memory. Can grow only in one side.
Insert/Remove:
O(1) - at the end.
O(n) - at the begining/middle, because we need to shift the rest of the elements.
Search: O(n).
Random access: Yes.
When there is no space to push_back a new element the vector is reallocated (usually x2 or x1.5), old elements get copyed and the new element is added.
2.Deque is a dynamically allocated array (not contiguous) in memory, can grow in both sides. It can be implemented as array of arrays - the outter array contains pointers to inner arrays which hold data. When we want to push_back or push_front an element and there is no space in the current inner vector we do not reallocate old vectors but allocate new contiguous blocks of memory (arrays) and put its pointer in the outter vector (at the front/back of the deque) and place the new element.
Insert/Remove:
O(1) - at the begining and end.
O(n) - at the middle, because we need to shift the rest of the elements.
Search: O(n).
Random access: Yes.
Data is not contiguous - slightly slower then vectors. But if data type is complex - probably managing objects of such data type is more consumming, in this case Deque is preferable (no copying needed).
Recomendation:
Frequent push_front - deque
Build-in data type - vector
Not build-in data type - deque
Unpredictable growth - deque
Frequently passed to C functions - vector (as we can get access to elements of the vector by int *p = &vec[0]
Vector vs Deque
3.List is a doubly linked list. It consists of not contiguous memory blocks. Consumes more memory as each element holds two pointers.
Insert/Remove:
O(1) - at any place (but if the position is not at the begining of end first we need to find it = O(n)).
Search: O(n).
Random access: No.
Provides efficient function - splice O(1).
Traversing is slow comparing to vectors or deques. Because of more frequent cache-misses (data is not contiguous).
4.Forward list is a linked list.
5.Array. Its size can not be changed.
Associative containers - binary tree (elements are sorted).
They called Associative because elements consist of a key and a value associated with the key.
1.Map - key, value pairs. No duplicated keys. Sorted by key.
Insert/Remove:
O(log(n)) - at any place.
Search: O(log(n)).
Random access: Yes. Operator [] is implemented. Use [key] or auto it = map_.find(key) to get needed element.
Keys can not be modified.
Traversing is slow comparing to vectors or deques. Because of more frequent cache-misses.
2.Multimap - map which can contain duplicated keys.
Insert/Remove:
O(log(n)) - at any place.
Search: O(log(n)).
Random access: No. Operator [] is not implemented. Use auto it = map_.find(key) to get needed element.
Keys can not be modified.
Traversing is slow comparing to vectors or deques. Because of more frequent cache-misses.
Set/Multiset are special cases of Map/Multimap when key=value.
3.Set - no duplicated items.
Insert/Remove:
O(log(n)) - at any place.
Search: O(log(n)).
No. Operator [] is not implemented. Use auto it = set_.find(value) to get needed element.
Values can not be modified.
Traversing is slow comparing to vectors or deques. Because of more frequent cache-misses.
4.Multiset - set which can contain duplicated items.
Insert/Remove:
O(log(n)) - at any place.
Search: O(log(n)).
Random access: No. Operator [] is not implemented. Use auto it = set_.find(value) to get needed element.
Values can not be modified.
Traversing is slow comparing to vectors or deques. Because of more frequent cache-misses.
Unordered containers - implemented as hash table.
1.Unordered set - no duplicates, no []; multiset - allows to contain duplicate elements, no [].
2.Unordered map - no duplicates, []; multimap - allows to contain duplicate keys, no [].
Insert/Remove:
O(1) - at any place.
Search: O(1).
Values in sets and keys in maps can not be modified.
Hash collision can degrade O(1) performance as several keys can go into one bucket and in order to keep it we need to insert it at the end of the linked list. The worse case when all elements go into one bucket:
Associative arrays - can be implemented as a map/unordered_map.
[] operator returns a reference with write rights. Multimap/unordered_multimap can’t be used to implement associative array because they don’t have [] operator.
Unordered containers provide at least forward iterators. Depends on the STL implementation it can provide Bidirectional iterators.
Every container provides iterators and const iterators. Const iterators provide read-only rights to the container elements.
Iterator functions (can be applied to non-random access iterators):
Iterator adoptors:
1.Insert iterator
2.Stream iterator
3.Reverse iterator
Algorithms
Non-modifying Algorithms
Almost all algorithms have generalized version - take one more argument - predicate.
1.Counting algorithms
2.Min/max element
3.Linear search. Returns an iterator for the first matching element. If there is no match then returned iterator == vec.end()
4.Check attributes
Modifying Algorithms
1.Copy
2.Move
If a container holds objects of a class with Move constructor the move algorithm works using move semantic otherwise copy is performed.
3.Transform
4.Swap - two-way copying
5.Fill
6.Replace
7.Remove. Only _copy algorithms work. Don’t know why.
8.Reverse
9.Rotate
10.Shuffle. Include for default_random_engine.
String
Stream
C++ Input/Output library.
Stream is a serial IO interface to external devices (file, stdin/stdout, network, etc.).
File system stream:
Formatted input - read a file and treat its data with respect to variable type.
Formatting:
Unformatted IO - tread all data as text, read/write char symbols.
std::endl
Stream buffer
We can create custom IO streams (override). Example, redirecting:
Member functions vs Algorithms
In most cases member functions are preferable over algorithms, because they know about underlaying data structure and can take advantage of it. Examples:
Find
Remove
Reverse iterators
We can get a reverse iterator from a standard iterator:
When we convert a revese iterator to an iterator (base() member function), the result iterator will be the reverse iterator moved one element to the right. When we convert an iterator to a reverse iterator (vector<int>::revers_iterator(it)), the result reverse iterator will be the iterator moved one element to the left.
Remove operation
We can remove elements from a container using algorithms or member functions.
Vectors don’t have remove member function:
Lists have an efficient remove member function, O(n) to find elements and O(1) to relink elements:
Removing elements from ordered associative containers (map/multimap, set/multiset) can be done in O(log(n)) by using erase member function (instead of O(n) using remove algorithm):
Vector od Deque: remove algorithm + erase member function to remove redundant elements.
List: member function remove.
Associative containers (ordered/unordered): member function erase.