C++进阶之一篇文章教会你什么是map和set

C++进阶之一篇文章教会你什么是map和set_第1张图片

map和set

  • 序列式容器和关联式容器
  • 键值对
  • 树形结构的关联式容器
  • set
    • 1.set模板参数列表
    • 2.set的构造
    • 3.set迭代器
    • 4.set容器函数
    • 5.set修改器函数
    • 6.set观察器和操作成员函数
    • 7.set应用场景
  • set和multiset的区别
  • map
    • 1.map模板参数列表
    • 2.map的构造
    • 3.map迭代器
    • 4.map容量和元素访问函数
    • 5.map修改器函数
    • 6.map观察器和操作成员函数
    • 7.map应用场景
  • map和multimap的区别

序列式容器和关联式容器

序列式容器:

序列式容器是一组用于存储数据的容器,其中的数据按照它们在容器中的位置进行存储和访问。序列式容器提供了对元素的线性访问和操作,其主要特点包括:

  1. 线性存储: 序列式容器按照元素在容器中的插入顺序线性存储。元素在容器中的位置是固定的,与键值无关。
  2. 支持重复元素: 序列式容器允许存储重复的元素,同一个值可以出现多次。
  3. 提供随机访问: 序列式容器支持随机访问,可以通过索引或迭代器快速访问任何位置的元素。
  4. 不排序元素: 序列式容器不会自动对元素进行排序,元素按照插入的顺序存储。

C++标准库中的序列式容器包括 std::vectorstd::dequestd::liststd::array 等。这些容器适用于需要按顺序处理数据的情况,如数组、列表、队列等。

总结

  • 关联式容器适用于需要高效的查找和排序的情况,数据按键值有序存储。
  • 序列式容器适用于需要线性存储和随机访问的情况,元素按插入顺序存储。
  • 选择容器类型取决于具体的需求和操作。关联式容器适用于需要快速查找和排序的情况,而序列式容器适用于需要按顺序处理数据的情况。

关联式容器:

关联式容器是一组用于存储数据的容器,其中的数据按照某种规则关联起来,通常是基于键值的关联。关联式容器提供了一种高效的方式来查找、插入和删除元素,其主要特点包括:

  1. 基于键值的存储: 关联式容器通过键值来存储和访问元素。每个元素都有一个关联的键,通过该键可以唯一标识和访问元素。
  2. 有序存储: 关联式容器通常会按照键值的顺序进行排序。这使得元素可以按照一定的顺序进行迭代,例如升序或降序。
  3. 高效的查找: 关联式容器提供了高效的查找操作。由于元素按键值有序存储,可以使用二分查找等算法来快速找到特定键对应的元素。
  4. 不允许重复键: 关联式容器通常不允许存储重复的键值。每个键只能对应一个元素。

C++标准库中的关联式容器包括 std::setstd::multisetstd::mapstd::multimap 等。这些容器适用于需要高效查找和排序的情况,如字典、集合等。

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量keyvaluekey代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义

SGI-STL中关于键值对的定义

template <class T1, class T2>
struct pair
{
    typedef T1 first_type;
    typedef T2 second_type;
    T1 first;
    T2 second;
    pair(): first(T1()), second(T2())
    {}
    pair(const T1& a, const T2& b): first(a), second(b)
    {}
};

树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构

C++标准库提供了四种主要的树形结构关联式容器,它们分别是 std::setstd::multisetstd::mapstd::multimap。这些容器都是基于树的数据结构,通常使用红黑树实现,用于存储键值对,并提供高效的查找和排序功能。以下是对这四种容器的详细解释:

  1. std::set
    • std::set 是一个集合容器,它存储唯一的键值(key),不允许重复的键。
    • 元素按照键值自动排序(通常升序),这使得查找和插入操作都非常高效。
    • 提供了 inserterasefind 等操作来管理元素集合。
  2. std::multiset
    • std::multisetstd::set 类似,但允许存储重复的键值。因此,它可以包含相同的键多次。
    • 元素按照键值自动排序,查找和插入操作也非常高效。
    • 提供了 inserterasefind 等操作。
  3. std::map
    • std::map 是一个键值对容器,每个键都唯一对应一个值。
    • 元素按照键值自动排序,通常按照键的升序顺序排列。
    • 提供了 inserterasefind 等操作来管理键值对。
  4. std::multimap
    • std::multimapstd::map 类似,但允许存储相同的键对应不同的值,因此一个键可以对应多个值。
    • 元素按照键值自动排序,查找和插入操作非常高效。
    • 提供了 inserterasefind 等操作。

这些容器的共同特点是它们的元素是按照键值有序存储的,这使得它们适用于需要快速查找和自动排序的情况。它们使用平衡二叉树(通常是红黑树)来实现这些功能,保持了插入、删除和查找的平均时间复杂度为 O(log N)

选择哪种容器取决于需求。如果需要存储唯一的键或键值对,可以选择 std::setstd::map。如果需要允许重复的键或键对应多个值,可以选择 std::multisetstd::multimap。这些容器提供了丰富的操作,可以满足各种数据组织和访问的需求。

set

1.set模板参数列表

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T>      // set::allocator_type
           > class set;
  1. T(class T):这是模板的第一个参数,表示存储在set容器中的元素的数据类型。set会存储类型为T的元素。
  2. Compare(class Compare = less:这是模板的第二个参数,表示用于比较元素之间的顺序的比较函数。默认情况下,它采用less,它使用元素的小于运算符<来进行比较。您可以提供自定义的比较函数,以定义元素之间的排序规则。这个比较函数必须是一个可调用的二元谓词,它接受两个参数(const T&类型)并返回一个bool值,指示它们的顺序。
  3. Alloc(class Alloc = allocator:这是模板的第三个参数,表示用于分配和管理内存的分配器类型。默认情况下,它采用std::allocator,这是C++标准库提供的分配器。您可以提供自定义的分配器类型,以满足特定的内存管理需求。

std::set是一个有序容器,它以红黑树Red-Black Tree)作为底层数据结构来存储元素。红黑树是一种自平衡二叉搜索树,保持了元素的有序性,并且允许高效的插入、删除和查找操作。std::set中的元素是唯一的,即相同的元素只会被存储一次。

成员类型

C++进阶之一篇文章教会你什么是map和set_第2张图片

2.set的构造

关于C++标准库中std::set容器构造函数的不同形式和重载,以及它们的用途的解释:

  1. explicit set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());

    • 这是默认构造函数,创建一个空的std::set容器。
    • 参数comp用于指定比较函数,它用于确定元素的顺序。默认情况下,使用key_compare(),即容器类型中指定的比较函数。
    • 参数alloc用于指定分配器,用于管理内存。默认情况下,使用allocator_type(),即容器类型中指定的分配器。
    #include 
    #include 
    
    int main() {
        // 使用默认构造函数创建一个空的 set
        std::set<int> mySet;
        
        // 向 set 中插入元素
        mySet.insert(5);
        mySet.insert(2);
        mySet.insert(8);
    
        // 输出 set 的内容
        for (const int& value : mySet) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    
  2. template set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());

    • 这是范围构造函数,创建一个std::set容器,并初始化其内容,从范围 [first, last) 中的元素。
    • 参数firstlast是迭代器,用于指定范围。容器将包含该范围内的元素。
    • 参数compalloc与默认构造函数中的含义相同,用于指定比较函数和分配器。
    #include 
    #include 
    #include 
    
    int main() {
        // 创建一个包含 vector 中元素的 set
        std::vector<int> vec = {3, 1, 4, 1, 5, 9};
        std::set<int> mySet(vec.begin(), vec.end());
    
        // 输出 set 的内容
        for (const int& value : mySet) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    
  3. set (const set& x);

    • 这是拷贝构造函数,创建一个新的std::set容器,并使用另一个set容器x的内容初始化它。
    • 这个构造函数用于创建一个副本,将x中的所有元素复制到新容器中。
    #include 
    #include 
    
    int main() {
        // 创建一个 set 并初始化
        std::set<int> set1 = {1, 2, 3};
    
        // 使用拷贝构造函数创建一个新的 set,复制 set1 的内容
        std::set<int> set2(set1);
    
        // 输出 set2 的内容
        for (const int& value : set2) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    
        return 0;
    }
    

    这些构造函数提供了不同的方式来初始化和创建std::set容器,以适应不同的需求。您可以根据需要选择适当的构造函数来创建和初始化std::set容器,以存储和管理数据。

赋值运算符重载

set& operator= (const set& x); 是C++标准库中std::set容器的赋值运算符(operator=)重载函数的签名。

这个赋值运算符用于将一个std::set容器的内容复制给另一个std::set容器。具体来说,它将右操作数(x,另一个std::set容器)的内容复制到左操作数(调用该运算符的std::set容器)中,并返回左操作数的引用。

下面是赋值运算符的使用示例:

std::set<int> set1 = {1, 2, 3};
std::set<int> set2;

// 使用赋值运算符将 set1 的内容复制到 set2
set2 = set1;

// 现在 set2 包含了 set1 的内容

在上面的示例中,set2 = set1 使用赋值运算符将set1的内容复制到set2,使得set2包含了与set1相同的元素。

需要注意的是,赋值运算符会删除左操作数原来的内容,并用右操作数的内容替代它。这意味着在赋值之后,左操作数的内容将与右操作数相同,而原来的内容将被销毁。

这个赋值运算符对于将一个std::set容器的内容复制到另一个容器非常有用,可以用于容器之间的数据交换和拷贝。

3.set迭代器

这些函数是C++标准库中std::set容器的成员函数,用于获取不同类型的迭代器和常量迭代器,以便遍历std::set容器的元素。以下是这些迭代器和常量迭代器的描述:

  1. begin:返回一个迭代器,指向std::set容器中第一个元素的位置。
  2. end:返回一个迭代器,指向std::set容器中超出最后一个元素的位置。
  3. rbegin:返回一个反向迭代器,指向std::set容器中最后一个元素的位置。使用反向迭代器可以逆序遍历容器。
  4. rend:返回一个反向迭代器,指向std::set容器中超出第一个元素的位置。它通常与rbegin一起使用,以定义逆序遍历的结束点。
  5. cbegin:返回一个常量迭代器,指向std::set容器中第一个元素的位置。常量迭代器用于遍历容器并防止修改容器中的元素。
  6. cend:返回一个常量迭代器,指向std::set容器中超出最后一个元素的位置。
  7. crbegin:返回一个常量反向迭代器,指向std::set容器中最后一个元素的位置。常量反向迭代器用于逆序遍历容器,并防止修改容器中的元素。
  8. crend:返回一个常量反向迭代器,指向std::set容器中超出第一个元素的位置。通常与crbegin一起使用,以定义逆序遍历的结束点。

示例

#include 
#include 

int main() {
    // 创建一个 std::set 容器并初始化
    std::set<int> mySet = {5, 2, 8, 1, 9};

    // 使用 begin 和 end 迭代器遍历容器
    std::cout << "正序遍历:" << std::endl;
    for (std::set<int>::iterator it = mySet.begin(); it != mySet.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 使用 rbegin 和 rend 反序遍历容器
    std::cout << "逆序遍历:" << std::endl;
    for (std::set<int>::reverse_iterator rit = mySet.rbegin(); rit != mySet.rend(); ++rit) {
        std::cout << *rit << " ";
    }
    std::cout << std::endl;

    // 使用 cbegin 和 cend 常量迭代器遍历容器
    std::cout << "使用常量迭代器:" << std::endl;
    for (std::set<int>::const_iterator cit = mySet.cbegin(); cit != mySet.cend(); ++cit) {
        std::cout << *cit << " ";
    }
    std::cout << std::endl;

    // 使用 crbegin 和 crend 常量反序遍历容器
    std::cout << "使用常量反序迭代器:" << std::endl;
    for (std::set<int>::const_reverse_iterator crit = mySet.crbegin(); crit != mySet.crend(); ++crit) {
        std::cout << *crit << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出

正序遍历:
1 2 5 8 9 
逆序遍历:
9 8 5 2 1 
使用常量迭代器:
1 2 5 8 9 
使用常量反序迭代器:
9 8 5 2 1 

这个示例演示了如何使用不同类型的迭代器来遍历std::set容器中的元素。正序遍历使用beginend迭代器,逆序遍历使用rbeginrend反向迭代器,常量迭代器使用cbegincend,常量反序迭代器使用crbegincrend。这些迭代器允许您以不同的方式访问容器中的元素,并提供了对元素的只读访问(对于常量迭代器)或读写访问(对于非常量迭代器)

4.set容器函数

这里是关于C++标准库中std::set容器的容量相关成员函数的解释和示例:

  1. empty(测试容器是否为空):

    • bool empty() const 是一个公有成员函数,用于测试容器是否为空。
    • 如果容器为空,返回 true;否则,返回 false

    示例:

    std::set<int> mySet;
    if (mySet.empty()) {
        std::cout << "容器为空" << std::endl;
    } else {
        std::cout << "容器不为空" << std::endl;
    }
    
  2. size(返回容器大小):

    • size_type size() const 是一个公有成员函数,用于返回容器中元素的数量。
    • 返回一个无符号整数类型 (size_type),表示容器中元素的个数。

    示例:

    std::set<int> mySet = {1, 2, 3, 4, 5};
    std::cout << "容器的大小为: " << mySet.size() << std::endl;
    
  3. max_size(返回最大可能大小):

    • size_type max_size() const 是一个公有成员函数,用于返回容器的最大可能大小。
    • 返回一个无符号整数类型 (size_type),表示容器可以容纳的最大元素数量,这取决于系统和编译器。

    示例:

    std::set<int> mySet;
    std::cout << "容器的最大可能大小为: " << mySet.max_size() << std::endl;
    

这些容量相关的成员函数允许您查询容器的状态,例如检查容器是否为空、获取容器的大小以及了解容器可以容纳的最大元素数量。这对于在使用容器时进行状态检查和资源规划非常有用。

5.set修改器函数

std::set容器的修改器(Modifiers)相关成员函数,它们用于修改std::set容器的内容:

  1. insert(插入元素):
    • std::pair insert(const value_type& val);
    • iterator insert(iterator position, const value_type& val);
    • template void insert(InputIterator first, InputIterator last);
    • 这些成员函数用于向std::set容器中插入元素。它们允许插入单个元素或一组元素。
    • 如果插入成功,insert函数返回一个迭代器指向插入的元素(或已存在的元素),以及一个bool值指示是否插入成功。
  2. erase(删除元素):
    • iterator erase(iterator position);
    • size_type erase(const key_type& key);
    • iterator erase(iterator first, iterator last);
    • 这些成员函数用于从std::set容器中删除元素。您可以提供要删除的元素的位置或键(key)。
    • erase函数返回一个迭代器,指向被删除元素之后的位置。
  3. swap(交换内容):
    • void swap(set& x);
    • 这个成员函数用于交换两个std::set容器的内容,使它们互相包含对方的元素。
  4. clear(清除内容):
    • void clear();
    • 这个成员函数用于清除容器中的所有元素,使容器变为空。
  5. emplace(构造并插入元素):
    • template pair emplace(Args&&... args);
    • 这个成员函数用于通过构造新元素并插入容器来添加新元素。它通过接受参数包(parameter pack)来构造元素,并返回插入的元素迭代器和一个bool值指示是否插入成功。
  6. emplace_hint(构造并插入元素,带有提示位置):
    • template iterator emplace_hint(iterator position, Args&&... args);
    • 这个成员函数与emplace类似,但允许提供一个插入位置的提示(iterator position),以便更高效地插入新元素。

示例

#include 
#include 

int main() {
    // 创建一个 std::set 容器并初始化
    std::set<int> mySet = {5, 2, 8, 1, 9};

    // 输出容器的内容
    std::cout << "容器的内容:";
    for (const int& value : mySet) {
        std::cout << " " << value;
    }
    std::cout << std::endl;

    // 使用 insert 插入元素
    std::pair<std::set<int>::iterator, bool> result1 = mySet.insert(3); // 插入元素3
    std::pair<std::set<int>::iterator, bool> result2 = mySet.insert(5); // 尝试插入重复元素5

    if (result1.second) {
        std::cout << "成功插入元素3" << std::endl;
    } else {
        std::cout << "元素3已存在" << std::endl;
    }

    if (result2.second) {
        std::cout << "成功插入元素5" << std::endl;
    } else {
        std::cout << "元素5已存在" << std::endl;
    }

    // 输出修改后的容器内容
    std::cout << "修改后的容器内容:";
    for (const int& value : mySet) {
        std::cout << " " << value;
    }
    std::cout << std::endl;

    // 使用 erase 删除元素
    mySet.erase(2); // 删除元素2

    // 输出删除元素后的容器内容
    std::cout << "删除元素后的容器内容:";
    for (const int& value : mySet) {
        std::cout << " " << value;
    }
    std::cout << std::endl;

    // 使用 emplace 插入元素(构造并插入)
    mySet.emplace(6); // 插入元素6

    // 输出插入元素后的容器内容
    std::cout << "插入元素后的容器内容:";
    for (const int& value : mySet) {
        std::cout << " " << value;
    }
    std::cout << std::endl;

    return 0;
} 

输出

容器的内容: 1 2 5 8 9
成功插入元素3
元素5已存在
修改后的容器内容: 1 2 3 5 8 9
删除元素后的容器内容: 1 3 5 8 9
插入元素后的容器内容: 1 3 5 6 8 9

6.set观察器和操作成员函数

Observers

  1. key_comp(返回键的比较对象):
    • key_compare key_comp() const;
    • 返回一个键的比较对象,用于比较容器中键的大小关系。
  2. value_comp(返回值的比较对象):
    • value_compare value_comp() const;
    • 返回一个值的比较对象,用于比较容器中的值的大小关系。

Operations

  1. find(查找元素):
    • iterator find(const key_type& key);
    • 返回一个迭代器,指向键等于给定键的元素。如果未找到元素,则返回指向容器末尾的迭代器 end()
  2. count(计算特定值的元素个数):
    • size_type count(const key_type& key);
    • 返回具有指定值的元素个数。通常情况下,由于键是唯一的,结果将是0或1。
  3. lower_bound(返回下限迭代器):
    • iterator lower_bound(const key_type& key);
    • 返回一个迭代器,指向第一个大于或等于给定键的元素位置。
  4. upper_bound(返回上限迭代器):
    • iterator upper_bound(const key_type& key);
    • 返回一个迭代器,指向第一个大于给定键的元素位置。
  5. equal_range(获取相等元素的范围):
    • pair equal_range(const key_type& key);
    • 返回一个包含两个迭代器的pair,第一个迭代器指向第一个等于给定键的元素,第二个迭代器指向第一个大于给定键的元素。

示例

#include 
#include 

int main() {
    std::set<int> mySet = {1, 2, 3, 4, 5};

    // 使用 key_comp 获取键的比较对象
    std::set<int>::key_compare keyComp = mySet.key_comp();

    // 使用 value_comp 获取值的比较对象
    std::set<int>::value_compare valueComp = mySet.value_comp();

    // 查找元素
    std::set<int>::iterator it = mySet.find(3);
    if (it != mySet.end()) {
        std::cout << "找到元素 3" << std::endl;
    } else {
        std::cout << "未找到元素 3" << std::endl;
    }

    // 计算特定值的元素个数
    size_t count = mySet.count(4);
    std::cout << "值为 4 的元素个数: " << count << std::endl;

    // 获取下限迭代器
    std::set<int>::iterator lower = mySet.lower_bound(2);
    std::cout << "下限迭代器指向大于或等于 2 的元素位置: " << *lower << std::endl;

    // 获取上限迭代器
    std::set<int>::iterator upper = mySet.upper_bound(4);
    std::cout << "上限迭代器指向大于 4 的元素位置: " << *upper << std::endl;

    // 获取相等元素的范围
    std::pair<std::set<int>::iterator, std::set<int>::iterator> range = mySet.equal_range(3);
    std::cout << "相等元素范围: [" << *(range.first) << ", " << *(range.second) << "]" << std::endl;

    return 0;
}

7.set应用场景

#include 
void TestSet()
{
    // 用数组array中的元素构造set
    int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,6, 8, 0 };
    set<int> s(array, array+sizeof(array)/sizeof(array));
    cout << s.size() << endl;
    // 正向打印set中的元素,从打印结果中可以看出:set可去重
    for (auto& e : s)
    cout << e << " ";
    cout << endl;
    // 使用迭代器逆向打印set中的元素
    for (auto it = s.rbegin(); it != s.rend(); ++it)
    cout << *it << " ";
    cout << endl;
    // set中值为3的元素出现了几次
    cout << s.count(3) << endl;
}

set和multiset的区别

std::setstd::multiset 都是 C++ 标准库中的关联式容器,它们用于存储一组元素,且元素是按照一定的顺序排列的。然而,它们之间有一个关键的区别:

  1. 唯一性:
    • std::setstd::set 中的元素是唯一的,不允许重复的元素存在。如果试图插入重复的元素,新元素将不会被插入。
    • std::multisetstd::multiset 允许存储重复的元素,可以插入多个具有相同键值的元素。

下面是更详细的比较:

  • std::set
    • 元素是唯一的,不允许重复。
    • 插入操作会检查元素是否已存在,如果已存在则插入不成功。
    • 查找操作在 O(log n) 的时间内完成,因为元素唯一。
    • 适用于需要维护一组唯一值的情况,例如去重操作。
  • std::multiset
    • 允许存储重复的元素,不会检查重复性。
    • 插入操作总是成功,允许存储多个相同键值的元素。
    • 查找操作在 O(log n) 的时间内完成,因为仍然具有排序特性。
    • 适用于需要允许重复值的情况,例如统计元素出现次数。
    • multiset在底层实际存储的是的键值对。

在选择使用 std::set 还是 std::multiset 时,取决于你的数据需求。如果需要维护一组唯一的值,使用 std::set;如果允许重复值,并且需要统计重复值的出现次数,使用 std::multiset

map

1.map模板参数列表

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;

std::map是一个关联式容器,它提供了key-value对的存储和管理。以下是代码中各个模板参数的解释:

  • Key:表示std::map容器中的键类型,也就是键值对中的键的类型。
  • T:表示std::map容器中的值类型,也就是键值对中的值的类型。
  • Compare:表示比较键的方式的函数对象,默认使用 std::less,可以根据需要自定义键的比较方式。
  • Alloc:表示用于分配内存的分配器类型,默认使用 std::allocator>,通常情况下不需要自定义。

通过这个模板声明,你可以创建不同类型的std::map容器,以存储不同类型的key-value对。例如,如果要创建一个存储整数键和字符串值的std::map容器,可以这样使用:

std::map<int, std::string> myMap;

这将创建一个std::map容器,其中键的类型是整数 (int),值的类型是字符串 (std::string)。您可以使用该容器来存储和检索整数和字符串之间的映射关系。

成员类型

C++进阶之一篇文章教会你什么是map和set_第3张图片

2.map的构造

  1. empty(默认构造函数):
    • explicit map(const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
    • 这是std::map的默认构造函数,它创建一个空的std::map对象。
    • comp 参数是可选的,表示用于比较键的自定义比较函数,默认使用 key_compare(),即使用std::less
    • alloc 参数是可选的,表示用于内存分配的自定义分配器,默认使用 allocator_type(),即使用std::allocator>
  2. range(范围构造函数):
    • template map(InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
    • 这个构造函数允许您从一个范围(由迭代器 firstlast 定义)初始化std::map容器。
    • comp 参数是可选的,表示用于比较键的自定义比较函数,默认使用 key_compare()
    • alloc 参数是可选的,表示用于内存分配的自定义分配器,默认使用 allocator_type()
  3. copy(复制构造函数):
    • map(const map& x);
    • 这个构造函数用于创建一个新的std::map容器,并以另一个std::map容器 x 的内容进行初始化,即复制x中的键值对到新的容器中。

示例

#include 
#include 

int main() {
    // 使用默认构造函数创建一个空的 std::map
    std::map<int, std::string> myMap1;
    
    // 使用范围构造函数创建并初始化 std::map
    std::map<int, std::string> myMap2 = {{1, "One"}, {2, "Two"}, {3, "Three"}};

    // 使用复制构造函数创建一个与 myMap2 相同内容的副本
    std::map<int, std::string> myMap3(myMap2);

    // 输出容器内容
    std::cout << "myMap1: " << myMap1.size() << " elements" << std::endl;
    std::cout << "myMap2: " << myMap2.size() << " elements" << std::endl;
    std::cout << "myMap3: " << myMap3.size() << " elements" << std::endl;

    return 0;
}

在此示例中,我们演示了三种不同的std::map构造函数的使用方式,包括默认构造函数、范围构造函数和复制构造函数。根据构造函数的不同用途,可以创建空容器、初始化容器或复制另一个容器的内容。

赋值运算符重载

将一个 std::map 的内容复制到另一个已存在的 std::map 中。例如:

std::map<int, std::string> sourceMap = {{1, "One"}, {2, "Two"}};
std::map<int, std::string> targetMap;
targetMap = sourceMap; // 使用拷贝赋值运算符将内容复制到目标容器

它会创建一个新的 std::map 容器,其中包含与原始容器相同的键值对。这是一种将数据从一个容器复制到另一个容器的常见方式。

3.map迭代器

std::map容器提供了多种迭代器,用于访问容器中的元素。以下是这些迭代器的详细描述:

  1. begin
    • iterator begin();
    • 返回一个迭代器,指向std::map容器中第一个元素的位置。
  2. end
    • iterator end();
    • 返回一个迭代器,指向std::map容器中的末尾(尾后位置)。
  3. rbegin
    • reverse_iterator rbegin();
    • 返回一个逆向迭代器,指向std::map容器中的最后一个元素的位置。
  4. rend
    • reverse_iterator rend();
    • 返回一个逆向迭代器,指向std::map容器中的逆向末尾(逆向尾后位置)。
  5. cbegin
    • const_iterator cbegin() const;
    • 返回一个常量迭代器,指向std::map容器中第一个元素的位置。用于只读访问。
  6. cend
    • const_iterator cend() const;
    • 返回一个常量迭代器,指向std::map容器中的末尾(尾后位置)。用于只读访问。
  7. crbegin
    • const_reverse_iterator crbegin() const;
    • 返回一个常量逆向迭代器,指向std::map容器中的最后一个元素的位置。用于只读访问。
  8. crend
    • const_reverse_iterator crend() const;
    • 返回一个常量逆向迭代器,指向std::map容器中的逆向末尾(逆向尾后位置)。用于只读访问。

示例

#include 
#include 

int main() {
    // 创建一个 std::map 容器并初始化
    std::map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};

    // 使用正向迭代器遍历容器
    std::cout << "正向迭代器遍历容器:" << std::endl;
    for (std::map<int, std::string>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
        std::cout << "键: " << it->first << ", 值: " << it->second << std::endl;
    }

    // 使用逆向迭代器遍历容器
    std::cout << "逆向迭代器遍历容器:" << std::endl;
    for (std::map<int, std::string>::reverse_iterator rit = myMap.rbegin(); rit != myMap.rend(); ++rit) {
        std::cout << "键: " << rit->first << ", 值: " << rit->second << std::endl;
    }

    // 使用常量迭代器遍历容器(只读访问)
    std::cout << "常量迭代器遍历容器:" << std::endl;
    for (std::map<int, std::string>::const_iterator cit = myMap.cbegin(); cit != myMap.cend(); ++cit) {
        std::cout << "键: " << cit->first << ", 值: " << cit->second << std::endl;
    }

    // 使用常量逆向迭代器遍历容器(只读访问)
    std::cout << "常量逆向迭代器遍历容器:" << std::endl;
    for (std::map<int, std::string>::const_reverse_iterator crit = myMap.crbegin(); crit != myMap.crend(); ++crit) {
        std::cout << "键: " << crit->first << ", 值: " << crit->second << std::endl;
    }

    return 0;
}

这个示例演示了如何使用不同类型的迭代器遍历std::map容器中的元素:

  • 使用正向迭代器 begin()end() 遍历容器。
  • 使用逆向迭代器 rbegin()rend() 逆向遍历容器。
  • 使用常量迭代器 cbegin()cend() 以只读方式遍历容器。
  • 使用常量逆向迭代器 crbegin()crend() 以只读方式逆向遍历容器。

4.map容量和元素访问函数

这些是关于std::map容器的一些容量和元素访问相关的成员函数:

  1. empty(检查是否为空):
    • bool empty() const;
    • 用于检查std::map容器是否为空。如果容器为空,返回true;否则返回false
  2. size(返回容器大小):
    • size_type size() const;
    • 返回std::map容器中键值对的数量,也就是容器的大小。
  3. max_size(返回最大容量):
    • size_type max_size() const;
    • 返回std::map容器可以容纳的最大元素数量。这通常受到系统内存限制的影响。
  4. operator[](访问元素):
    • T& operator[](const key_type& key);
    • 允许通过键访问容器中的元素。如果键存在,则返回相应的值的引用,如果不存在,则会插入一个具有该键的默认值,并返回引用。请注意,如果键不存在,将插入一个具有默认值的键值对。
  5. at(访问元素):
    • T& at(const key_type& key);
    • 允许通过键访问容器中的元素,类似于 operator[]。但与 operator[] 不同,如果键不存在,at 会抛出 std::out_of_range 异常。

示例

#include 
#include 

int main() {
    std::map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};

    // 检查容器是否为空
    if (myMap.empty()) {
        std::cout << "容器为空" << std::endl;
    } else {
        std::cout << "容器不为空" << std::endl;
    }

    // 获取容器的大小
    std::cout << "容器大小为: " << myMap.size() << std::endl;

    // 访问容器中的元素
    std::string value = myMap[2]; // 使用 operator[]
    std::cout << "键 2 对应的值为: " << value << std::endl;

    try {
        std::string value = myMap.at(4); // 键 4 不存在,会抛出异常
    } catch (const std::out_of_range& e) {
        std::cerr << "键 4 不存在: " << e.what() << std::endl;
    }

    return 0;
}

在此示例中,我们演示了如何使用std::map容器的容量和元素访问成员函数,包括检查容器是否为空、获取容器大小、通过键访问元素以及处理键不存在的情况。

5.map修改器函数

  1. insert(插入元素):
    • pair insert(const value_type& value);
    • 用于插入键值对(键值对的类型为 value_type)到std::map容器中。如果插入成功,返回一个迭代器指向插入的元素和 true;如果元素已存在,则返回一个迭代器指向现有元素和 false
  2. erase(删除元素):
    • iterator erase(iterator position);
    • size_type erase(const key_type& key);
    • 用于删除容器中的元素。第一种形式删除由迭代器 position 指定的元素,并返回指向下一个元素的迭代器。第二种形式删除指定键的元素,并返回删除的元素数量。
  3. swap(交换内容):
    • void swap(map& x);
    • 用于交换两个std::map容器的内容,即交换键值对。交换后,两个容器的内容完全交换。
  4. clear(清空内容):
    • void clear();
    • 用于清空std::map容器中的所有元素,使容器成为空容器。
  5. emplace(构造并插入元素):
    • pair emplace(Args&&... args);
    • 允许构造并插入元素,其中 Args 是键值对的构造参数。如果插入成功,返回一个迭代器指向插入的元素和 true;如果元素已存在,则返回一个迭代器指向现有元素和 false
  6. emplace_hint(构造并插入元素带提示):
    • iterator emplace_hint(const_iterator position, Args&&... args);
    • 允许构造并插入元素,其中 Args 是键值对的构造参数,并且可以提供插入位置的提示。返回一个迭代器指向插入的元素。

示例

#include 
#include 

int main() {
    std::map<int, std::string> myMap;

    // 插入元素
    myMap.insert(std::make_pair(1, "One"));
    myMap.insert(std::make_pair(2, "Two"));
    
    // 删除元素
    myMap.erase(2);

    // 交换容器内容
    std::map<int, std::string> anotherMap = {{3, "Three"}, {4, "Four"}};
    myMap.swap(anotherMap);

    // 清空容器
    myMap.clear();

    // 构造并插入元素
    myMap.emplace(5, "Five");

    // 使用 emplace_hint 插入元素
    std::map<int, std::string>::iterator it = myMap.emplace_hint(myMap.begin(), 6, "Six");

    // 输出容器内容
    for (const auto& pair : myMap) {
        std::cout << "键: " << pair.first << ", 值: " << pair.second << std::endl;
    }

    return 0;
}

在此示例中,我们演示了如何使用std::map容器的修改器成员函数,包括插入、删除、交换和清空元素,以及使用 emplaceemplace_hint 插入元素的方式。

6.map观察器和操作成员函数

Observers

  1. key_comp(返回键的比较对象):
    • key_compare key_comp() const;
    • 返回一个键的比较对象,用于比较容器中键的大小关系。
  2. value_comp(返回值的比较对象):
    • value_compare value_comp() const;
    • 返回一个值的比较对象,用于比较容器中的值的大小关系。

Operations

  1. find(查找元素):
    • iterator find(const key_type& key);
    • 返回一个迭代器,指向键等于给定键的元素。如果未找到元素,则返回指向容器末尾的迭代器 end()
  2. count(计算特定键的元素个数):
    • size_type count(const key_type& key);
    • 返回具有指定键的元素个数。通常情况下,由于键是唯一的,结果将是0或1。
  3. lower_bound(返回下限迭代器):
    • iterator lower_bound(const key_type& key);
    • 返回一个迭代器,指向第一个大于或等于给定键的元素位置。
  4. upper_bound(返回上限迭代器):
    • iterator upper_bound(const key_type& key);
    • 返回一个迭代器,指向第一个大于给定键的元素位置。
  5. equal_range(获取相等元素的范围):
    • pair equal_range(const key_type& key);
    • 返回一个包含两个迭代器的pair,第一个迭代器指向第一个等于给定键的元素,第二个迭代器指向第一个大于给定键的元素。

以下是示例用法

#include 
#include 

int main() {
    std::map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}};

    // 使用 find 查找元素
    std::map<int, std::string>::iterator it = myMap.find(2);
    if (it != myMap.end()) {
        std::cout << "找到键 2,值为: " << it->second << std::endl;
    } else {
        std::cout << "未找到键 2" << std::endl;
    }

    // 使用 count 计算特定键的元素个数
    size_t count = myMap.count(3);
    std::cout << "键 3 的元素个数: " << count << std::endl;

    // 使用 lower_bound 获取下限迭代器
    std::map<int, std::string>::iterator lower = myMap.lower_bound(2);
    std::cout << "下限迭代器指向键 2,值为: " << lower->second << std::endl;

    // 使用 upper_bound 获取上限迭代器
    std::map<int, std::string>::iterator upper = myMap.upper_bound(2);
    std::cout << "上限迭代器指向大于键 2 的元素,值为: " << upper->second << std::endl;

    // 使用 equal_range 获取相等元素的范围
    std::pair<std::map<int, std::string>::iterator, std::map<int, std::string>::iterator> range = myMap.equal_range(2);
    std::cout << "相等元素范围: [" << range.first->second << ", " << range.second->second << "]" << std::endl;

    return 0;
}

在此示例中,我们演示了如何使用std::map容器的观察器和操作成员函数,包括查找元素、计算特定键的元素个数、获取下限迭代器、获取上限迭代器以及获取相等元素的范围。这些函数使得在std::map容器中执行查找和搜索操作变得非常方便。

7.map应用场景

#include 
#include 
void TestMap()
{
	map<string, string> m;
	// 向map中插入元素的方式:
	// 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
	m.insert(pair<string, string>("peach", "桃子"));
	// 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
	m.insert(make_pair("banan", "香蕉"));
	// 借用operator[]向map中插入元素
	/*
	operator[]的原理是:
	用构造一个键值对,然后调用insert()函数将该键值对插入到map中
	如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
	如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
	operator[]函数最后将insert返回值键值对中的value返回
	*/
	// 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引用结果,
		m["apple"] = "苹果";
	// key不存在时抛异常
	//m.at("waterme") = "水蜜桃";
		cout << m.size() << endl;
		// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
		for (auto& e : m)
			cout << e.first << "--->" << e.second << endl;
		cout << endl;
		// map中的键值对key一定是唯一的,如果key存在将插入失败
		auto ret = m.insert(make_pair("peach", "桃色"));
		if (ret.second)
			cout << "不在map中, 已经插入" << endl;
		else
			cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
			<< ret.first->second << " 插入失败" << endl;
		// 删除key为"apple"的元素
		m.erase("apple");
		if (1 == m.count("apple"))
			cout << "apple还在" << endl;
		else
			cout << "apple被吃了" << endl;
}

map和multimap的区别

std::mapstd::multimap 都是 C++ 标准库中的关联式容器,它们用于存储一组key-value对(键值对),并根据键对元素进行排序。它们之间的主要区别在于唯一性和允许重复性:

std::map

  • std::map 是一个关联容器,其中的键是唯一的,每个键只能关联一个值。
  • 如果试图插入已存在的键,新值将覆盖旧值。
  • 使用 operator[]insert 插入元素。
  • 查找操作(例如 findcountlower_boundupper_bound)返回匹配的唯一元素。

std::multimap

  • std::multimap 也是一个关联容器,允许多个具有相同键的值存在。
  • 可以插入多个具有相同键的值,不会覆盖旧值。
  • 使用 insert 插入元素。
  • 查找操作返回匹配的所有元素,因为可以有多个相同键的元素。

以下是示例用法和比较:

std::map 示例:

#include 
#include 

int main() {
    std::map<int, std::string> myMap;

    // 插入键值对
    myMap[1] = "One";
    myMap[2] = "Two";
    myMap[3] = "Three";

    // 重复键值对将覆盖旧值
    myMap[2] = "New Two";

    // 查找键为 2 的元素
    std::map<int, std::string>::iterator it = myMap.find(2);
    if (it != myMap.end()) {
        std::cout << "找到键 2,值为: " << it->second << std::endl;
    }

    return 0;
}

std::multimap 示例:

#include 
#include 

int main() {
    std::multimap<int, std::string> myMultiMap;

    // 插入多个具有相同键的值
    myMultiMap.insert(std::make_pair(1, "One"));
    myMultiMap.insert(std::make_pair(2, "Two"));
    myMultiMap.insert(std::make_pair(2, "Another Two"));
    myMultiMap.insert(std::make_pair(3, "Three"));

    // 查找键为 2 的所有元素
    auto range = myMultiMap.equal_range(2);
    for (auto it = range.first; it != range.second; ++it) {
        std::cout << "找到键 2,值为: " << it->second << std::endl;
    }

    return 0;
}

总结

  • 如果需要维护唯一key-value对,使用 std::map
  • 如果允许具有相同键的多个值存在,使用 std::multimap

你可能感兴趣的:(C++进阶,c++,开发语言,数据结构)