Reimplementation of Standard Template Library's containers.
- Installation
- Introduction to STL
- Red-Black Tree
- Iterators
- Sequence Containers
- Associative Containers
- Unordered Associative Containers
- Container Adaptors
Install and run tests:
git clone https://github.com/georghegel/containers.git && cd containers
make test
Check for memory leaks:
make leaks
Usage:
#include "containers.h"
int main(){
s21::set a = {1,2,3,4,5}; // creates the set (1,2,3,4,5)
a.insert(6); // adds 6 to it (1,2,3,4,5,6)
a.erase(5); // removes 5 from the set (1,2,3,4,6)
a.insert(6); // does nothing because we already have 6 (1,2,3,4,6)
size_t sz = a.size(); // returns set's size: 5
auto bg = a.begin(); // returns begin iterator
auto it = a.find(3); // returns an iterator to the found element, otherwise nullptr
}
The Standard Template Library (STL) in C++ provides a variety of containers, each designed to handle different types of data storage and access patterns efficiently. Here is a list of the main STL containers along with a brief description of each:
As of 07.07.2024 I've only implemented set and map. Will implement other ones in the future
std::vector
std::deque
std::list
std::forward_list
std::array
std::set
std::multiset
std::map
std::multimap
std::unordered_set
std::unordered_multiset
std::unordered_map
std::unordered_multimap
std::stack
std::queue
std::priority_queue
Under the set and the map containers we have data structure that will contain our data.
For computational efficiency both of these containers have Red-Black Tree as a data structure.
In worst case for insertion and deletion we have:
By Cormen et al. book we have next properties of the RBT:
A red-black tree is a binary search tree with one extra bit of storage per node: its color, which can be either
$${\color{red}RED}$$ or$${\color{black}BLACK}$$
By constraining the way nodes can be colored on any path from the root to a leaf, red-black trees ensure that no such path is more than twice as long as any other, so that the tree is approximately balanced.
Each node of the tree now contains the fields color, key, left, right, and p. If a child or the parent of a node
does not exist, the corresponding pointer field of the node contains the value NIL.
We shall regard these NIL'S as being pointers to external nodes (leaves) of the binary search tree and the normal, key-bearing nodes as being internal nodes of the tree.
A binary search tree is a red-black tree if it satisfies the following red-black properties:
- Every node is either red or black.
- Every leaf (NIL) is black.
- If a node is red, then both its children are black.
- Every simple path from a node to a descendant leaf contains the same number of black nodes
For the full understanding:
We will not invent the wheel and will define iterators as it defined in a C++ Standard:
1.
Iterators are a generalization of pointers that allow a C++ program to work with different data structures
(for example, containers and ranges) in a uniform manner. To be able to construct template algorithms
that work correctly and efficiently on different types of data structures, the library formalizes not just the
interfaces but also the semantics and complexity assumptions of iterators. An input iterator i supports the
expression *i, resulting in a value of some object type T, called the value type of the iterator. An output
iterator i has a non-empty set of types that are indirectly_writable to the iterator; for each such type T,
the expression *i = o is valid where o is a value of type T. For every iterator type X, there is a corresponding
signed integer-like type (23.3.4.4) called the difference type of the iterator.
2.
Since iterators are an abstraction of pointers, their semantics are a generalization of most of the semantics of
pointers in C++. This ensures that every function template that takes iterators works as well with regular
pointers. This document defines six categories of iterators, according to the operations defined on them: input
iterators, output iterators, forward iterators, bidirectional iterators, random access iterators, and contiguous
iterators, as shown in Table 83.
Table 83: Relations among iterator categories [tab:iterators.relations]
Each higher level iterator contains all properties of the previous levels.
For example, Bidirectional iterator has the properties of the Forward, which has all properties of the I/O.
The input_iterator concept defines requirements for a type whose referenced values can be read (from the requirement for indirectly_readable (23.3.4.2)) and which can be both pre- and post-incremented.
Means that we can iterate over ++a and a++ operators.
And also can read the values by "*" operator.
The output_iterator concept defines requirements for a type that can be used to write values (from the requirement for indirectly_writable (23.3.4.3)) and which can be both pre- and post-incremented.
Same as above + we can write in it.
The forward_iterator concept adds copyability, equality comparison, and the multi-pass guarantee.
Added equality comparison (multi-pass guarantee I haven't researched yet)
Use cases: forward_list
The bidirectional_iterator concept adds the ability to move an iterator backward as well as forward.
Added: --a and a--
Use cases: set, map, multiset, multimap, list, queue
The random_access_iterator concept adds support for constant-time advancement with +=, +, -=, and -, as well as the computation of distance in constant time with -. Random access iterators also support array notation via subscripting.
New properties and operations are given below:
template<class I>
concept random_access_iterator =
bidirectional_iterator<I> &&
derived_from<ITER_CONCEPT(I), random_access_iterator_tag> &&
totally_ordered<I> &&
sized_sentinel_for<I, I> &&
requires(I i, const I j, const iter_difference_t<I> n) {
{ i += n } -> same_as<I&>;
{ j + n } -> same_as<I>;
{ n + j } -> same_as<I>;
{ i -= n } -> same_as<I&>;
{ j - n } -> same_as<I>;
{ j[n] } -> same_as<iter_reference_t<I>>;
};
Use cases: vector, deque
The contiguous_iterator concept provides a guarantee that the denoted elements are stored contiguously in memory.
Haven't learned yet
Use cases: array
[1] Standard Template Library
[2] Algorithms
[3] Iterators
[4] Red Black Tree
[5] C++ Standard