C++中的unordered_multiset容器详解

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. 性能提示

  1. 平均情况下查找、插入、删除时间复杂度为O(1)O(1)O(1)
  2. 最坏情况下(哈希冲突严重)时间复杂度退化为O(n)O(n)O(n)
  3. 负载因子过高会影响性能,可适时rehash()
  4. 自定义类型需要提供哈希函数和相等比较
  5. 迭代器在插入操作后可能失效(重新哈希时)

13. 与multiset比较

特性 unordered_multiset multiset
实现方式 哈希表 红黑树
元素顺序 无序 自动排序
查找复杂度 平均O(1)O(1)O(1) O(log⁡2n)O(\log_2 n)O(log2n)
内存使用 通常较少 通常较多
迭代器稳定性 插入可能失效 稳定(除删除元素)

14. 与unordered_set比较

特性 unordered_multiset unordered_set
元素唯一性 允许重复 不允许重复
count()返回值 可能大于111 000或111
equal_range() 常用于处理重复 较少使用
相关推荐
豆瓣鸡8 分钟前
算法日记 - Day3
java·开发语言·算法
韭菜炒鸡肝天43 分钟前
VTK开发笔记(一):VTK介绍,Qt..+VSx+VTK.编译
开发语言·笔记·qt
Aaron - Wistron1 小时前
Web API C# (Furion版)带 单元测试
开发语言·后端·c#
Dxy12393102162 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
0566463 小时前
Python康复训练——常用标准库
开发语言·python·学习
骊城英雄3 小时前
基于C#+avalonia ui实现的跨平台点胶机灌胶监控控制上位机软件
开发语言·ui·c#
吾儿良辰3 小时前
一个被BCL遗忘的高性能集合:C# CircularBuffer<T>深度解析
开发语言·windows·c#
昆曲之源_娄江河畔3 小时前
Python如何安装flask, pymssql
开发语言·python·flask·pymssql
Leighteen3 小时前
`try-finally` 里的 `return`:为什么 `finally` 会悄悄改掉返回值、吞掉异常
java·开发语言
0566463 小时前
Python康复训练——控制流与函数
开发语言·python·学习