Associative containers always store data in sorted fashion. Associative containers are very efficient in searching a value with O( log n ) runtime complexity because of its sorted nature which allows us to perform binary search.
=> How are associative containers implemented ? 
  Associative containers are implemented through
  self balancing binary search trees, more precisely
  red black trees.
When we insert an element in the container,
  it will be inserted in the tree in it proper location and if we remove an
  element from the container, the tree will be rearranged/balanced so that
  everything is still in order.
The default sorting criteria for
  associative containers is '<'(less than), though a custom criteria
  can also be provided.
There are 4 associative containers in C++ STL
  :
1. set
2. map
3. multiset
4. multimap
1. set and multiset
         
       | 
    
  Set cannot have duplicate items so if you try to insert an already
  existing value, it will not be inserted.
set<int> st;            
    // create a set of integers
=> Inserting an element
  st.insert(5);            // st
    = {5};           
    
st.insert(1);           
    // st= {1,5};
st.insert(3);       
        // st= {1,3,5};
After insertion of every element, the data in set is
  automatically sorted. 
Time complexity :
  O(log n)    , irrespective of where the value is
  inserted (in beginning, in middle or somewhere else) because it takes
  logarithmic time to find its correct position in sorted order in a binary
  search tree.
This member function returns a pair of values :
pair<set<int>::iterator, bool>
the bool value indicates whether insertion is successful(1) or it
  failed(0) because of duplicate value.
If it succeeded, then the
  iterator points to this new value, else if it failed it points to the existing
  duplicate value corresponding to the value we tried to insert. 
=> Searching an element
  This is one of the most important feature of associative container. They
  provide logarithmic time searching as compared to sequence containers which
  provide linear time searching.
set<int>::iterator it = st.find(3);
Time complexity : O(log n)
Note: sequence containers
  don't even provide a find function.
=> Erasing an element
  a. using iterator
st.erase(it);   
          
Time complexity : O(1)
  
b. erase by value
st.erase(3);            
Time complexity : O(log n)
This member function automatically finds the element in the set
  and erases it.
Note : sequence containers don't provide this
  kind of erase.
 
=> Traversing set
  We use the universal iterator traversal to traverse the elements(which is in
  sorted order).
for(auto it=st.begin();it!=st.end();it++)
  
     cout<<*it;
  Now. let's discuss multiset.
As you might be guessing,
  multiset is almost similar to set, it just has one difference that
  it allows duplicates.
It also stores the values in sorted order
  but may have multiple occurrences of the same value(unlike set).
multiset<int> ms;
ms.insert(3);   
                // ms={3};
ms.insert(5);   
                // ms = {3,5};
ms.insert(3);   
                // ms =
    {3,3,5};
All the functions provided by set are also provided by
  multiset.
Note : A very important point to remember is that
  both set and multiset are read-only, i.e values in the container can
  not be modified.
auto it=st.find(10);
*it = 10;        
                // assignment not
    allowed.
Why ?
Think about this, if we modify any value inside
  a set/multiset, the container may get corrupted as the elements may not be
  sorted anymore .The container might lose its most important property due to
  which it offered its advantages.
Properties of set/multiset :
  
1. Fast searching of elements. Time complexity : O(log n)
2.
  No random access operator ( like vector/deque)
3. Slow traversal
2. map and multimap
  Sometimes we don't want to sort the container by values of elements, but by
  some key provided for every value.
They have the same interface as
  set/multiset except that instead of directly storing values they
  store key-value pairs.
Keys are actually used to position the elements in map/multimap,
  the value just corresponds to the key. 
map, just like set does not allow
  duplicate keys.
  Create a map from int(key) to char(value)
map<int,char> mp;    
=> Inserting a value
  A pair of values is inserted in map/multimap
Different forms :
1.
  mp.insert(pair<int,char>(10,'c'));
2. Use make_pair function
    mp.insert(make_pair(50,'b'));
3.
  pair<int,char> p = make_pair(25,'b');
   
    mp.insert(p);
4. Map also provides random access by key value using random access operator ('[ ]').
      mp[15]='c';    // inserts
    pair(15,'c') in map if not already present otherwise updates the value
 
Time Complexity : O(log n), similar to set
=> Searching a key
  mp.find(20);
Searches for this
  key and gets the key-value pair.
Time Complexity :
  O(log n) 
 => Traversing map
  Use universal iterator traversal to traverse in sorted order. We can access
  the key and value using the members of pair: first and second respectively.
for auto it=mp.begin();it!=mp.end();it++)
  
     cout<<(*it).first<<" "<<(*it).second;
(*it).first can also be
  written as it->first. Similarly,
(*it).second
  can also be written as it->second.
This form is preferred mostly as it's more easier and takes less
  time.
  multimap is similar to map, just the difference is that
  it allows duplicate keys.
Note for multiset and multimap :
  find() member function returns an iterator to one of the value(multiset) or
  key(multimap) in case of duplicates, which is typically the first
  one.    
Note for map/multimap :  Keys
  cannot be modified inside the container but their corresponding values can be
  changed.
auto it = mp.find(10);
(*it).first = '20';   
                 // error
(*it).second
    = 'f';              
    // No error
=> Now you might be wondering,
  why are all these containers called as "associative" ?
The
  term "associative" actually refers to the key-value nature of map which
  associates a value with a key. Think of set/multiset as special kind of map
  where the key of any element is the same as its value.
  => How to provide custom sorting criteria in associative containers?
  We need to use functor (a class/structure that overloads the ()
  operator so it can be called like a function).
struct comp {
    bool operator() (int x, int y)
    {       // this is our custom comparator which
    overrides default comparator
       
    return x>y;           
                   //
    sort by decreasing order
    }
};
Then declare set using this functor
set<int, comp> st;       
                   
    // this will sort the set in decreasing order
Note : sort in decreasing order can also be written as
  set<int, greater<int> > st;
  => Using associative container for user defined data types :
  If we wish to stored user defined data types in associative container, we
    must provide custom comparison function
  otherwise compiler shows error as there is no internal implementation
  available.
Consider a structure 
struct Person{
    int age;
  
     string name;
};
Let's define a set for Person.
We define structure
  'comp' as discussed before which provides custom comparison function. We sort
  the set by age.
struct comp {
    bool operator() (Person x, Person y)
    {       
       
    return x.age > y.age;
    }
};
Now declare set simply as:
set<Person, comp> st;
Other associative containers can also be implemented in similar
  way.
 
  => Finally before the end of the article, let's check out two
  important member functions provided by associative containers which are very
  commonly used for multiset and multimap :
lower_bound(val) and
  upper_bound(val).
lower_bound(val)
  : searches for the first position corresponding to the value(or key in
  multimap) 'val'.
upper_bound(val) :
  searches for the first position corresponding to the first greater value(or
  key in multimap) 'val'.
For example :
  To get total number of elements with value 10, use :
int total_count = st.upper_bound(10) - st.lower_bound(10);
This completes our discussion of Associative containers. If you have
  any doubts. please mention in the comments section.
Also check out other types of C++ STL containers if you missed them out :
1. Sequence Containers
2. Unordered Containers
Thank
  you for your patience reading. If you enjoyed this post, I’d be very grateful
  if you’d help it spread by emailing it to a friend, or sharing it on Whatsapp
  or Facebook.
😇Happy Learning!!



