Skip to content

关联容器

2.1 关联容器的定义和特点

关联容器是C++标准模板库(STL)中的一种容器类型,与顺序容器不同,关联容器中的元素不是按插入顺序存储的,而是按照键(key)自动排序并存储的。关联容器通过键来高效地访问元素,而不是通过位置索引。

主要特点:

  • 自动排序:元素在插入时根据键自动排序
  • 高效查找:基于键的查找操作非常高效(对数时间复杂度)
  • 键值对:部分关联容器(map/multimap)存储键值对
  • 唯一键:部分关联容器(set/map)要求键唯一
  • 多重键:部分关联容器(multiset/multimap)允许多个元素具有相同键

关联容器与顺序容器的区别:

特性顺序容器关联容器
元素存储按插入顺序按键自动排序
访问方式位置索引/迭代器键值查找
查找效率O(n)O(log n)
主要用途线性序列存储键值映射/集合

2.2 关联容器的分类

2.2.1 set

  • 存储唯一键的集合
  • 元素本身即为键
  • 自动排序
  • 头文件:#include <set>

2.2.2 multiset

  • 存储可重复键的集合
  • 元素本身即为键
  • 自动排序
  • 头文件:#include <set>

2.2.3 map

  • 存储键值对(key-value pairs)
  • 键唯一
  • 按键自动排序
  • 头文件:#include <map>

2.2.4 multimap

  • 存储键值对
  • 允许重复键
  • 按键自动排序
  • 头文件:#include <map>

2.3 关联容器的共同特性

2.3.1 构造函数

cpp
set<T> s;                         // 空set
set<T> s(begin, end);             // 从迭代器范围构造
set<T> s(initializer_list);       // 初始化列表构造

map<K, V> m;                      // 空map
map<K, V> m(begin, end);          // 从迭代器范围构造
map<K, V> m(initializer_list);    // 初始化列表构造

2.3.2 容量操作

cpp
bool empty() const;               // 容器是否为空
size_t size() const;              // 元素数量
size_t max_size() const;          // 最大可能元素数量

2.3.3 修改操作

cpp
// 插入元素
pair<iterator, bool> insert(const value_type& value);  // set
iterator insert(iterator hint, const value_type& value);
void insert(InputIt first, InputIt last);

// 删除元素
void erase(iterator pos);
size_type erase(const key_type& key);
void erase(iterator first, iterator last);

// 清空容器
void clear() noexcept;

2.3.4 查找操作

cpp
// 查找键
iterator find(const key_type& key);
const_iterator find(const key_type& key) const;

// 统计键出现次数
size_type count(const key_type& key) const;

// 返回键的边界
iterator lower_bound(const key_type& key);
const_iterator lower_bound(const key_type& key) const;
iterator upper_bound(const key_type& key);
const_iterator upper_bound(const key_type& key) const;
pair<iterator, iterator> equal_range(const key_type& key);

2.3.5 迭代器

cpp
iterator begin() noexcept;
const_iterator begin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;
reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;
reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;

2.4 set和multiset详解

2.4.1 set

  • 存储唯一键的集合
  • 元素按升序自动排序
  • 插入重复元素会被忽略
  • 查找效率高(O(log n))

常用操作示例

cpp
#include <iostream>
#include <set>

int main()
{
    // 创建set
    std::set<int> mySet;
    
    // 插入元素
    mySet.insert(30);
    mySet.insert(10);
    mySet.insert(20);
    mySet.insert(10); // 重复元素,不会被插入
    
    // 遍历元素(自动排序)
    std::cout << "Set elements: ";
    for(const auto& elem : mySet) {
        std::cout << elem << " "; // 输出: 10 20 30
    }
    std::cout << std::endl;
    
    // 查找元素
    auto it = mySet.find(20);
    if(it != mySet.end()) {
        std::cout << "Found: " << *it << std::endl;
    }
    
    // 删除元素
    mySet.erase(10);
    
    // 检查元素是否存在
    if(mySet.count(10) {
        std::cout << "10 exists" << std::endl;
    } else {
        std::cout << "10 does not exist" << std::endl;
    }
    
    // 大小
    std::cout << "Set size: " << mySet.size() << std::endl;
    
    return 0;
}

2.4.2 multiset

  • 存储可重复键的集合
  • 元素按升序自动排序
  • 允许插入重复元素
  • 查找效率高(O(log n))

常用操作示例

cpp
#include <iostream>
#include <set>

int main()
{
    // 创建multiset
    std::multiset<int> myMultiSet;
    
    // 插入元素(允许重复)
    myMultiSet.insert(30);
    myMultiSet.insert(10);
    myMultiSet.insert(20);
    myMultiSet.insert(10); // 重复元素,会被插入
    
    // 遍历元素(自动排序)
    std::cout << "Multiset elements: ";
    for(const auto& elem : myMultiSet) {
        std::cout << elem << " "; // 输出: 10 10 20 30
    }
    std::cout << std::endl;
    
    // 统计元素出现次数
    std::cout << "Count of 10: " << myMultiSet.count(10) << std::endl;
    
    // 删除所有10
    myMultiSet.erase(10);
    
    // 检查元素是否存在
    std::cout << "Count of 10 after erase: " << myMultiSet.count(10) << std::endl;
    
    return 0;
}

2.5 map和multimap详解

2.5.1 map

  • 存储键值对(key-value pairs)
  • 键唯一
  • 按键自动排序
  • 通过键快速访问值
  • 插入重复键会覆盖原有值

常用操作示例

cpp
#include <iostream>
#include <map>
#include <string>

int main()
{
    // 创建map
    std::map<std::string, int> studentScores;
    
    // 插入元素
    studentScores["Alice"] = 95;
    studentScores["Bob"] = 88;
    studentScores["Charlie"] = 92;
    studentScores["Alice"] = 97; // 更新Alice的分数
    
    // 遍历元素(按键自动排序)
    std::cout << "Student scores:" << std::endl;
    for(const auto& pair : studentScores) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    
    // 访问元素
    std::cout << "Alice's score: " << studentScores["Alice"] << std::endl;
    
    // 查找元素
    auto it = studentScores.find("Bob");
    if(it != studentScores.end()) {
        std::cout << "Found Bob: " << it->second << std::endl;
    }
    
    // 删除元素
    studentScores.erase("Charlie");
    
    // 检查键是否存在
    if(studentScores.count("Charlie")) {
        std::cout << "Charlie exists" << std::endl;
    } else {
        std::cout << "Charlie does not exist" << std::endl;
    }
    
    // 大小
    std::cout << "Number of students: " << studentScores.size() << std::endl;
    
    return 0;
}

2.5.2 multimap

  • 存储键值对
  • 允许重复键
  • 按键自动排序
  • 通过键访问值,但可能有多个值

常用操作示例

cpp
#include <iostream>
#include <map>
#include <string>

int main()
{
    // 创建multimap
    std::multimap<std::string, std::string> departmentEmployees;
    
    // 插入元素(允许重复键)
    departmentEmployees.insert({"IT", "Alice"});
    departmentEmployees.insert({"IT", "Bob"});
    departmentEmployees.insert({"HR", "Charlie"});
    departmentEmployees.insert({"IT", "David"});
    
    // 遍历元素(按键自动排序)
    std::cout << "Department employees:" << std::endl;
    for(const auto& pair : departmentEmployees) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    
    // 查找特定键的所有值
    auto range = departmentEmployees.equal_range("IT");
    std::cout << "IT department employees:" << std::endl;
    for(auto it = range.first; it != range.second; ++it) {
        std::cout << it->second << std::endl;
    }
    
    // 统计键的数量
    std::cout << "IT employees count: " << departmentEmployees.count("IT") << std::endl;
    
    // 删除特定键的所有元素
    departmentEmployees.erase("HR");
    
    // 检查键是否存在
    if(departmentEmployees.find("HR") == departmentEmployees.end()) {
        std::cout << "HR department has no employees" << std::endl;
    }
    
    return 0;
}

2.6 自定义排序规则

关联容器默认使用std::less进行排序,但我们可以自定义排序规则:

cpp
#include <iostream>
#include <set>
#include <functional>

// 自定义比较函数
struct CaseInsensitiveCompare {
    bool operator()(const std::string& a, const std::string& b) const {
        return std::lexicographical_compare(
            a.begin(), a.end(),
            b.begin(), b.end(),
            [](char c1, char c2) {
                return std::tolower(c1) < std::tolower(c2);
            }
        );
    }
};

int main()
{
    // 使用自定义比较函数的set
    std::set<std::string, CaseInsensitiveCompare> caseInsensitiveSet;
    
    // 插入元素
    caseInsensitiveSet.insert("Apple");
    caseInsensitiveSet.insert("banana");
    caseInsensitiveSet.insert("apple"); // 不会插入,因为"Apple"和"apple"被视为相同
    
    // 遍历元素
    for(const auto& elem : caseInsensitiveSet) {
        std::cout << elem << " "; // 输出: Apple banana
    }
    std::cout << std::endl;
    
    // 查找元素(不区分大小写)
    auto it = caseInsensitiveSet.find("BANANA");
    if(it != caseInsensitiveSet.end()) {
        std::cout << "Found: " << *it << std::endl;
    }
    
    return 0;
}

2.7 关联容器的性能特点

操作set/mapmultiset/multimap时间复杂度
插入唯一键允许重复键O(log n)
删除按键删除按键删除O(log n)
查找按键查找按键查找O(log n)
遍历有序遍历有序遍历O(n)
空间额外指针额外指针O(n)

2.8 关联容器的选择指南

需求推荐容器
存储唯一键的集合set
存储可重复键的集合multiset
键值对映射,键唯一map
键值对映射,键可重复multimap
需要自定义排序规则任何关联容器+自定义比较器
需要最高效的查找任何关联容器(对数时间)
需要按键范围查询map/multimap

关联容器提供了高效的键值存储和查找能力,是C++标准库中处理映射和集合问题的重要工具。

知识如风,常伴吾身