C++中的unordered_multiset容器详解
1. unordered_multiset概述
unordered_multiset是C++11引入的关联容器,基于哈希表实现,允许存储重复元素,提供快速的查找、插入和删除操作,平均时间复杂度为O(1)O(1)O(1)。
2. 基本特性
- 哈希表实现:使用哈希函数组织元素
- 允许重复元素:容器中可以包含多个相同值
- 无序存储:元素不以任何特定顺序存储
- 快速访问:平均情况下提供常数时间复杂度的查找
- 动态大小:可以根据需要自动扩展
3. 头文件与声明
cpp
复制代码
#include <unordered_set>
using namespace std;
unordered_multiset<int> ums1; // 空unordered_multiset
unordered_multiset<string> ums2 = {"a", "b", "a"}; // 初始化列表(允许重复)
unordered_multiset<double> ums3(10); // 初始桶数为10
4. 构造函数与初始化
4.1 默认构造
cpp
复制代码
unordered_multiset<int> numbers;
4.2 范围构造
cpp
复制代码
int arr[] = {1, 2, 2, 3, 3, 3};
unordered_multiset<int> nums(arr, arr+6);
4.3 拷贝构造
cpp
复制代码
unordered_multiset<int> ums2(ums1);
4.4 自定义哈希函数和相等比较
cpp
复制代码
struct CaseInsensitiveHash {
size_t operator()(const string& s) const {
size_t h = 0;
for(char c : s) {
h += tolower(c);
}
return h;
}
};
struct CaseInsensitiveEqual {
bool operator()(const string& a, const string& b) const {
if(a.length() != b.length()) return false;
for(size_t i = 0; i < a.length(); ++i) {
if(tolower(a[i]) != tolower(b[i])) return false;
}
return true;
}
};
unordered_multiset<string, CaseInsensitiveHash, CaseInsensitiveEqual> case_insensitive_ms;
5. 容量操作
5.1 size()
cpp
复制代码
cout << ums.size(); // 返回元素总数量(包括重复)
5.2 empty()
cpp
复制代码
if(ums.empty()) {
cout << "unordered_multiset is empty";
}
5.3 max_size()
cpp
复制代码
cout << ums.max_size(); // 返回可容纳的最大元素数
6. 元素访问
6.1 迭代器访问
cpp
复制代码
for(auto it = ums.begin(); it != ums.end(); ++it) {
cout << *it << " ";
}
7. 修改操作
7.1 insert()
cpp
复制代码
ums.insert(10); // 插入单个元素
ums.insert({5, 5, 15}); // 插入初始化列表(允许重复)
ums.insert(arr, arr+3); // 插入范围
auto it = ums.insert(20); // 返回指向插入元素的迭代器
7.2 emplace()
cpp
复制代码
auto it = ums.emplace(30); // 原地构造元素
7.3 erase()
cpp
复制代码
ums.erase(5); // 删除所有值为5的元素
auto it = ums.find(10);
if(it != ums.end()) {
ums.erase(it); // 只删除一个10
}
ums.erase(ums.begin(), ums.end()); // 删除范围
7.4 clear()
cpp
复制代码
ums.clear(); // 清空所有元素
7.5 swap()
cpp
复制代码
unordered_multiset<int> ums2;
ums.swap(ums2); // 交换两个unordered_multiset
8. 查找操作
8.1 find()
cpp
复制代码
auto it = ums.find(10); // 返回指向第一个10的迭代器
if(it != ums.end()) {
cout << "Found: " << *it;
}
8.2 count()
cpp
复制代码
cout << ums.count(5); // 返回元素5的数量
8.3 equal_range()
cpp
复制代码
auto range = ums.equal_range(15); // 返回等于15的元素范围[pair]
for(auto it = range.first; it != range.second; ++it) {
cout << *it << " ";
}
9. 桶操作
9.1 bucket_count()
cpp
复制代码
cout << ums.bucket_count(); // 返回桶的数量
9.2 max_bucket_count()
cpp
复制代码
cout << ums.max_bucket_count(); // 返回最大桶数
9.3 bucket_size()
cpp
复制代码
cout << ums.bucket_size(2); // 返回第2个桶中的元素数
9.4 bucket()
cpp
复制代码
cout << ums.bucket("apple"); // 返回"apple"所在的桶索引
10. 哈希策略
10.1 load_factor()
cpp
复制代码
cout << ums.load_factor(); // 返回负载因子(元素数/桶数)
10.2 max_load_factor()
cpp
复制代码
cout << ums.max_load_factor(); // 返回最大负载因子
ums.max_load_factor(0.75); // 设置最大负载因子
10.3 rehash()
cpp
复制代码
ums.rehash(20); // 设置桶数为至少20
10.4 reserve()
cpp
复制代码
ums.reserve(100); // 预留空间至少容纳100个元素
11. 完整示例
cpp
复制代码
#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
int main() {
// 创建并初始化unordered_multiset
unordered_multiset<string> words = {"apple", "banana", "apple", "orange", "banana"};
// 插入元素
words.insert("grape");
words.emplace("pear");
words.insert({"apple", "kiwi", "kiwi"});
// 查找元素
cout << "Number of 'apple': " << words.count("apple") << endl;
auto found = words.find("orange");
if(found != words.end()) {
cout << "Found orange at bucket #" << words.bucket(*found) << endl;
}
// 遍历unordered_multiset
cout << "All words: ";
for(const auto& word : words) {
cout << word << " ";
}
cout << endl;
// 使用equal_range处理重复元素
cout << "All apples: ";
auto range = words.equal_range("apple");
for(auto it = range.first; it != range.second; ++it) {
cout << *it << " ";
}
cout << endl;
// 删除元素
words.erase("banana"); // 删除所有banana
auto it = words.find("kiwi");
if(it != words.end()) {
words.erase(it); // 只删除一个kiwi
}
// 桶信息
cout << "\nBucket information:" << endl;
cout << "Number of buckets: " << words.bucket_count() << endl;
cout << "Current load factor: " << words.load_factor() << endl;
// 调整哈希表
words.rehash(15);
cout << "After rehash, bucket count: " << words.bucket_count() << endl;
// 容量信息
cout << "\nSize: " << words.size() << endl;
cout << "Is empty: " << (words.empty() ? "Yes" : "No") << endl;
return 0;
}
12. 性能提示
- 平均情况下查找、插入、删除时间复杂度为O(1)O(1)O(1)
- 最坏情况下(哈希冲突严重)时间复杂度退化为O(n)O(n)O(n)
- 负载因子过高会影响性能,可适时
rehash()
- 自定义类型需要提供哈希函数和相等比较
- 迭代器在插入操作后可能失效(重新哈希时)
13. 与multiset比较
| 特性 |
unordered_multiset |
multiset |
| 实现方式 |
哈希表 |
红黑树 |
| 元素顺序 |
无序 |
自动排序 |
| 查找复杂度 |
平均O(1)O(1)O(1) |
O(log2n)O(\log_2 n)O(log2n) |
| 内存使用 |
通常较少 |
通常较多 |
| 迭代器稳定性 |
插入可能失效 |
稳定(除删除元素) |
14. 与unordered_set比较
| 特性 |
unordered_multiset |
unordered_set |
| 元素唯一性 |
允许重复 |
不允许重复 |
count()返回值 |
可能大于111 |
000或111 |
equal_range() |
常用于处理重复 |
较少使用 |