std::unordered_multiset is an unordered associative container in C++ STL that stores elements using a hash table and allows multiple occurrences of the same value. It provides efficient average-case insertion, deletion, and search operations while not maintaining any specific ordering of elements.
- Supports duplicate elements and performs insertion, deletion, and search operations in average-case O(1) time.
- Stores elements in an unordered manner using hashing rather than maintaining them in sorted order.
#include <bits/stdc++.h>
using namespace std;
int main() {
// Create an empty unordered multiset
unordered_multiset<int> ums1;
// Create and initialize elements
// using initialzer list
unordered_multiset<int> ums2 =
{5, 3, 4, 1, 1};
for(auto x : ums2)
cout << x << " ";;
return 0;
}
Output
1 1 4 3 5
Explanation: In this example, we initialize an unordered multiset in three ways:
- Statement unordered_multiset<int> ums1 creates an empty unordered multiset.
- Statement unordered_multiset<int> ums2 = {1, 1, 4, 3, 5} initialize the unordered multiset using initializer list.
Syntax
unordered_multiset<T> ums;
Where:
- T is the type of elements stored.
- ums is the name of the unordered multiset.
To use std::unordered_multiset, include the following header:
#include <unordered_set>
Basic Operations
The basic operations of unordered multiset are shown below:
Inserting Elements
Elements can be inserted into an unordered multiset using the insert() method. The position of the element is determined by the hashing function so we cannot specify any position while inserting.
#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_multiset<int> ums;
// Insert elements using insert()
ums.insert(5);
ums.insert(1);
ums.insert(3);
ums.insert(1);
ums.insert(2);
ums.insert(4);
for (auto x : ums) cout << x << ' ';
return 0;
}
Output
4 2 5 1 1 3
Explanation: Duplicate values are allowed and inserted into the container.
Accessing Elements
An unordered multiset does not allow direct access to elements by index as accessing elements is not the primary operation of this container. However, we have to increment or decrement iterator obtained from begin() or end() methods respectively to access the element by position. This can also be done with the help of next() or advance() function.
#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_multiset<int> ums =
{5, 1, 3, 2, 4, 1};
// Accessing element using iterator
auto it = next(ums.begin(), 2);
cout << *it;
return 0;
}
Output
3
Explanation: iterator it accesses the third element by advancing begin() by 2 positions using next().
Updating Elements
Unordered multiset does allow changing the value of the accessed elements as it may disrupt the container functions.
#include <iostream>
#include <unordered_set>
using namespace std;
int main() {
unordered_multiset<int> ums = {1, 2, 3, 4};
auto it = ums.find(2);
// Error: elements of unordered_multiset
// cannot be modified directly
*it = 10;
return 0;
}
Output
Compilation ErrorExplanation: Elements stored in an unordered_multiset cannot be modified directly because altering a value may change its hash value and violate the container's internal organization. Therefore, iterators of unordered_multiset provide read-only access to elements.
Finding Elements
It is the primary operation for which this container is optimized for. The find() method provides fast search of any element by value. This function returns iterator to the element if found, otherwise returns iterator to the end().
#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_multiset<int> ums =
{5, 1, 3, 4, 1};
// Finding 3
auto it = ums.find(3);
if (it != ums.end()) cout << *it;
else cout << "Element not Found!";
return 0;
}
Output
3
Traversing Elements
We can traverse unordered multiset either using range-based for loop or using begin() and end() iterator.
#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_multiset<int> ums =
{5, 1, 3, 4, 1};
// Using range-based for loop
for(auto x : ums)
cout << x << " ";
cout << "\n";
return 0;
}
Output
5 1 1 3 4
Deleting Elements
We can use erase() method to delete elements from unordered multiset. It deletes all occurrences of an elements if we provide the value of the element. If we provide iterator of the element, it deletes only that value from the unordered multiset.
#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_multiset<int> ums =
{5, 1, 3, 2, 4, 1};
// Delete all occurrences of element 1
ums.erase(1);
// Delete 2 using iterator
ums.erase(ums.find(2));
for (auto x: ums) cout << x << " ";
return 0;
}
Output
4 3 5
Explanation:
- ums.erase(1) deletes all occurrences of the element 1 from the set.
- ums.erase(ums.find(2)) deletes one occurrence of the element 2 by using an iterator returned from find(2).
Time Complexity
The below table lists the time complexity of the above operations on unordered multiset:
| Operation | Time Complexity |
|---|---|
| Inserting elements | O(1) average |
| Deleting elements | O(1) average |
| Finding elements by value | O(1) average |
| Accessing elements by position | O(n) |
| Traverse the multiset | O(n) |
Note: Due to hash collisions, the worst-case complexity of insertion, deletion, and searching can degrade to O(n).
Internal Working
std::unordered_multiset is implemented using a hash table.
- Each element is mapped to a bucket using a hash function.
- Elements with identical hash values are stored in the same bucket.
- Buckets are typically implemented using linked lists or similar structures.
- Hashing provides average-case constant time complexity for most operations.
Advantages of Unordered Multiset
std::unordered_multiset provides efficient storage and lookup for duplicate values.
- Allows storing multiple occurrences of the same element.
- Provides average-case constant-time insertion, deletion, and searching.
- Offers better performance than multiset when ordering is not required.
Limitations of Unordered Multiset
Despite its advantages, unordered_multiset has some limitations.
- Elements are not stored in sorted order.
- Performance can degrade due to excessive hash collisions.
- Does not support direct indexing or random access.
Common Member Functions
Following is the list of all member functions of std::unordered_multiset class in C++:
| Function | Description |
|---|---|
| insert() | Inserts elements into the container. |
| erase() | Removes elements from the container. |
| find() | Searches for an element. |
| count() | Returns the number of occurrences of a value. |
| size() | Returns the number of elements. |
| empty() | Checks whether the container is empty. |
| clear() | Removes all elements. |
| end(), end() | Return iterators for traversal. |
| equal_range() | Returns the range of elements matching a given value. |
| swap() | Swaps the contents of two unordered multisets. |
| bucket_count() | Returns the total number of buckets. |
| load_factor() | Returns the current load factor. |
| rehash() | Changes the number of buckets. |