详解map、set、multimap、multiset的使用

这篇具有很好参考价值的文章主要介绍了详解map、set、multimap、multiset的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者阿润菜菜
📖专栏C++



前言

map、set、multimap、multiset是C++ STL中的四种关联容器,它们内部都使用了红黑树这种高效的平衡检索二叉树来存储数据。它们的区别和用法如下:

  • map是一种键值对容器,它可以根据键来快速查找、插入和删除值,它的键是唯一的,不能重复。
  • multimap也是一种键值对容器,但它允许键重复,也就是说一个键可以对应多个值。
  • set是一种只存储值的容器,它可以快速查找、插入和删除值,它的值是唯一的,不能重复。在底层实际存放的是由<value, value>构成的键值对。(排序+去重)
  • multiset也是一种只存储值的容器,但它允许值重复,也就是说一个容器中可以有多个相同的值。

这四种容器都可以保证元素按照一定的顺序排列,通常是按照键或者值的升序排列。如果想要改变排序规则,可以自定义比较函数或者比较类。

这四种容器的时间复杂度如下:

  • 插入:O(log n)
  • 查找:O(log n)
  • 删除:O(log n)

如果想要更高效的时间复杂度,可以使用哈希表实现的unordered_map, unordered_set, unordered_multimap, unordered_multiset(后期介绍)。它们的时间复杂度如下:

  • 插入:O(1)期望,O(n)最坏
  • 查找:O(1)期望,O(n)最坏
  • 删除:O(1)期望,O(n)最坏
    但是哈希表实现的容器不能保证元素有序,并且需要提供合适的哈希函数和相等判断函数。

那什么是键值对呢?
键值对实际也是一个类,类里面对key和value数据进行了封装,key表示关键码,value表示与之对应的值,下面是SGI对于pair键值对结构体的定义:

struct pair{}有两个构造函数,一个是无参的利用T1和T2类型的默认构造函数初始化出来的键值对,一个是T1和T2作为参数的构造函数
另外pair还重载了两个运算符,用于键值对的等于和小于比较,小于比较时,优先比较first,如果first恰好满足x<y,则返回true。如果first之间相等,则比较失败返回false。如果first是x>y,那就继续比较second。
为了方便构造键值对,pair又另写了成员函数make_pair(),这个函数其实也是调用了构造函数,但在构造键值对的时候,省下我们自己去传T1和T2的类型了。

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) {}

template <class T1, class T2>
inline bool operator==(const pair<T1, T2>& x, const pair<T1, T2>& y) { 
  return x.first == y.first && x.second == y.second; 
}

template <class T1, class T2>
inline bool operator<(const pair<T1, T2>& x, const pair<T1, T2>& y) { 
  return x.first < y.first || (!(y.first < x.first) && x.second < y.second); 
}

template <class T1, class T2>
inline pair<T1, T2> make_pair(const T1& x, const T2& y) {
  return pair<T1, T2>(x, y);
}

set、multiset的使用

1. set

  • set是一种只存储值的容器,它可以快速查找、插入和删除值,它的值是唯一的,不能重复。
  • set内部使用红黑树这种高效的平衡检索二叉树来存储数据,因此它可以保证元素按照一定的顺序排列,通常是按照值的升序排列。
  • set的时间复杂度为O(log n),其中n是set中元素的个数。

所以set的特点是:不允许元素被修改,不允许元素有重复,那么它的作用就是排序+去重

链接set

set的使用方法如下:

  • 要使用set,需要添加set头文件,即#include<set>。除此之外,还需要在头文件下面加上一句:“using namespace std;”。
  • set的定义:可以使用set<类型> 变量名;来定义一个空的set对象,也可以使用set<类型> 变量名(初始值);来定义一个带有初始值的set对象。例如:
    • set<int> s1; // 定义一个空的set对象,存储int类型的元素
    • set<string> s2({"hello", "world"}); // 定义一个带有初始值的set对象,存储string类型的元素
  • set的插入:可以使用insert()方法来向set中插入一个元素,该方法返回一个pair对象,其中第一个元素是一个指向插入位置的迭代器,第二个元素是一个布尔值,表示插入是否成功。如果插入的元素已经存在于set中,则插入失败。例如:
    • s1.insert(1); // 向s1中插入1
    • s1.insert(2); // 向s1中插入2
    • auto p = s1.insert(2); // 再次向s1中插入2
    • cout << *p.first << " " << p.second << endl; // 输出 2 0,表示插入失败
  • set的删除:可以使用erase()方法来从set中删除一个元素或者一个范围内的元素,该方法返回一个整数,表示删除了多少个元素。如果删除的元素不存在于set中,则返回0。例如:
    • s2.erase("hello"); // 从s2中删除"hello"
    • s2.erase("hi"); // 从s2中删除"hi"
    • cout << s2.erase("world") << endl; // 输出 1,表示删除成功
    • cout << s2.erase("hi") << endl; // 输出 0,表示删除失败
  • set的查找:可以使用find()方法来查找set中是否存在某个元素,该方法返回一个指向找到元素的迭代器,如果没有找到,则返回end()。也可以使用count()方法来统计set中某个元素出现的次数,该方法返回一个整数,表示出现的次数。由于set中不允许重复元素,所以该方法只能返回0或者1。例如:
    • auto it = s1.find(1); // 查找s1中是否有1
    • if (it != s1.end()) cout << *it << endl; // 如果找到了,则输出1
    • cout << s1.count(2) << endl; // 输出 1,表示s1中有2
    • cout << s1.count(3) << endl; // 输出 0,表示s1中没有3
  • set的遍历:可以使用迭代器来遍历set中的所有元素,也可以使用范围for循环来遍历。由于set是有序的,所以遍历时会按照从小到大或者从大到小(取决于比较函数)的顺序输出元素。例如:
    • for (auto it = s1.begin(); it != s1.end(); ++it) cout << *it << " "; // 使用迭代器遍历s1,并输出每个元素
    • for (auto x : s2) cout << x << " "; // 使用范围for循环遍历s2,并输出每个元素
      代码示例:
#include <iostream>
#include <set>
using namespace std;

int main() {
  set<int> s1; // 定义一个空的set对象,存储int类型的元素
  set<string> s2({"hello", "world"}); // 定义一个带有初始值的set对象,存储string类型的元素

  s1.insert(1); // 向s1中插入1
  s1.insert(2); // 向s1中插入2
  auto p = s1.insert(2); // 再次向s1中插入2
  cout << *p.first << " " << p.second << endl; // 输出 2 0,表示插入失败

  s2.erase("hello"); // 从s2中删除"hello"
  s2.erase("hi"); // 从s2中删除"hi"
  cout << s2.erase("world") << endl; // 输出 1,表示删除成功
  cout << s2.erase("hi") << endl; // 输出 0,表示删除失败

  auto it = s1.find(1); // 查找s1中是否有1
  if (it != s1.end()) cout << *it << endl; // 如果找到了,则输出1
  cout << s1.count(2) << endl; // 输出 1,表示s1中有2
  cout << s1.count(3) << endl; // 输出 0,表示s1中没有3

  for (auto it = s1.begin(); it != s1.end(); ++it) cout << *it << " "; // 使用迭代器遍历s1,并输出每个元素
  cout << endl;
  
  for (auto x : s2) cout << x << " "; // 使用范围for循环遍历s2,并输出每个元素
  cout << endl;

  return 0;
}

2. multiset

  • multiset是一种只存储值的容器,它可以快速查找、插入和删除值,它允许值重复,也就是说一个容器中可以有多个相同的值。
  • multiset内部使用红黑树这种高效的平衡检索二叉树来存储数据,因此它可以保证元素按照一定的顺序排列,通常是按照值的升序排列。
  • multiset的时间复杂度为O(log n),其中n是multiset中元素的个数。

multiset相比于set,它可以存在重复的元素

multiset的使用方法和set基本相同:

  • 要使用multiset,需要添加set头文件,即#include<set>。除此之外,还需要在头文件下面加上一句:“using namespace std;”。
  • multiset的定义:可以使用multiset<类型> 变量名;来定义一个空的multiset对象,也可以使用multiset<类型> 变量名(初始值);来定义一个带有初始值的multiset对象。也可以指定一个比较函数对象来自定义元素的排序规则。例如:
    • multiset<int> ms1; // 定义一个空的multiset对象,存储int类型的元素,按照默认的升序排列
    • multiset<string> ms2({"hello", "world"}); // 定义一个带有初始值的multiset对象,存储string类型的元素,按照默认的升序排列
    • multiset<int, greater<int>> ms3; // 定义一个空的multiset对象,存储int类型的元素,按照降序排列
  • multiset的插入:可以使用insert()方法或者emplace()方法(C++11)来向multiset中插入一个元素,这两个方法都返回一个指向插入位置的迭代器。如果插入的元素已经存在于multiset中,则插入到已有元素之后。也可以使用emplace_hint()方法(C++11)来向multiset中插入一个元素,并提供一个提示位置作为参数,该方法返回一个指向插入位置的迭代器。如果提示位置是正确的或者接近正确的,则该方法比insert()或者emplace()更高效。例如:
    • ms1.insert(1); // 向ms1中插入1
    • ms1.insert(2); // 向ms1中插入2
    • ms1.insert(2); // 再次向ms1中插入2
    • auto it = ms1.insert(3); // 向ms1中插入3,并获取插入位置
    • cout << *it << endl; // 输出 3
    • ms2.emplace("hi"); // 向ms2中原位构造"hi"
    • ms2.emplace_hint(ms2.begin(), "bye"); // 向ms2中原位构造"bye",并提供一个提示位置
  • multiset的删除:可以使用erase()方法来从multiset中删除一个元素或者一个范围内的元素,该方法返回一个整数,表示删除了多少个元素。如果删除的元素不存在于multiset中,则返回0。也可以使用clear()方法来清空multiset中的所有元素。例如:
    • ms2.erase("hello"); // 从ms2中删除"hello"
    • ms2.erase("hi"); // 从ms2中删除"hi"
    • cout << ms2.erase("world") << endl; // 输出 1,表示删除成功
    • cout << ms2.erase("hi") << endl; // 输出 0,表示删除失败
    • ms2.clear(); // 清空ms2中的所有元素
  • multiset的查找:可以使用find()方法来查找multiset中是否存在某个元素,该方法返回一个指向找到元素的迭代器,如果没有找到,则返回end()。也可以使用count()方法来统计multiset中某个元素出现的次数,该方法返回一个整数,表示出现的次数。还可以使用lower_bound()方法和upper_bound()方法来获取某个元素在multiset中的边界位置,或者使用equal_range()方法来获取某个元素在multiset中的范围。例如:
    • auto it = ms1.find(1); // 查找ms1中是否有1
    • if (it != ms1.end()) cout << *it << endl; // 如果找到了,则输出1
    • cout << ms1.count(2) << endl; // 输出 2,表示ms1中有两个2
    • cout << ms1.count(3) << endl; // 输出 1,表示ms1中有一个3
    • auto lb = ms1.lower_bound(2); // 获取2在ms1中的下界位置
    • auto ub = ms1.upper_bound(2); // 获取2在ms1中的上界位置
    • cout << *lb << " " << *ub << endl; // 输出 2 3
    • auto p = ms1.equal_range(2); // 获取2在ms1中的范围
    • cout << *p.first << " " << *p.second << endl; // 输出 2 3
  • multiset的遍历:可以使用迭代器来遍历multiset中的所有元素,也可以使用范围for循环来遍历。由于multiset是有序的,所以遍历时会按照从小到大或者从大到小(取决于比较函数)的顺序输出元素。例如:
    • for (auto it = ms3.begin(); it != ms3.end(); ++it) cout << *it << " "; // 使用迭代器遍历ms3,并输出每个元素
    • for (auto x : ms3) cout << x << " "; // 使用范围for循环遍历ms3,并输出每个元素
      代码示例:
#include <iostream>
#include <set>
using namespace std;

int main() {
  multiset<int> ms1; // 定义一个空的multiset对象,存储int类型的元素,按照默认的升序排列
  multiset<string> ms2({"hello", "world"}); // 定义一个带有初始值的multiset对象,存储string类型的元素,按照默认的升序排列
  multiset<int, greater<int>> ms3; // 定义一个空的multiset对象,存储int类型的元素,按照降序排列

  ms1.insert(1); // 向ms1中插入1
  ms1.insert(2); // 向ms1中插入2
  ms1.insert(2); // 再次向ms1中插入2
  auto it = ms1.insert(3); // 向ms1中插入3,并获取插入位置
  cout << *it << endl; // 输出 3

  ms2.emplace("hi"); // 向ms2中原位构造"hi"
  ms2.emplace_hint(ms2.begin(), "bye"); // 向ms2中原位构造"bye",并提供一个提示位置

  ms2.erase("hello"); // 从ms2中删除"hello"
  ms2.erase("hi"); // 从ms2中删除"hi"
  cout << ms2.erase("world") << endl; // 输出 1,表示删除成功
  cout << ms2.erase("hi") << endl; // 输出 0,表示删除失败
  ms2.clear(); // 清空ms2中所有元素

  auto it = ms1.find(1); // 查找ms1中是否有1
  if (it != ms1.end()) cout << *it << endl; // 如果找到了,则输出1
  cout << ms1.count(2) << endl; // 输出 2,表示s有两个个
  cout << s.count(3) << endl; // 输出 0 表示s没有

  
  auto lb = s.lower_bound(4); 
   auto ub = s.upper_bound(6);
   cout<<*lb<<" "<<*ub<<endl;
   auto p=s.equal_range(5);
   cout<<*p.first<<" "<<*p.second<<endl;

3. 什么时候应该使用multiset而不是set

先看看二者有什么区别:

  • multiset允许元素重复,而set不允许元素重复
  • multiset的插入操作总是成功,而set的插入操作可能失败,如果插入的元素已经存在于set中
  • multiset的count()方法可能返回大于1的值,而set的count()方法只能返回0或者1。

set的作用:排序+去重,所以

  • 当你需要存储一些可以重复的元素,并且需要按照一定的顺序来访问它们时,你可以使用multiset。例如,你可以使用multiset来存储一些词汇,并且按照字典序来遍历它们,同时也可以统计每个词汇出现的次数。
  • 当你需要对一些元素进行合并或者分割操作时,你可以使用multiset。例如,你可以使用multiset来实现一个优先队列,每次从multiset中取出最小或者最大的元素,并且可以将两个或者多个元素合并成一个新的元素再插入到multiset中。
  • 当你需要使用一些set不提供的功能时,你可以使用multiset。例如,你可以使用multiset的extract()方法和merge()方法来将一个multiset中的结点转移到另一个multiset中,而不需要重新分配内存或者复制元素。

map、multimap的使用

1.map

map存储的是key和value组成的键值对元素结构体,在比较时按照key来进行比较下面源码我们可以看到key依旧是不允许被修改的,但其对应的value是可以被修改的

详解map、set、multimap、multiset的使用
map中比较时比较的是key类型,但我们可以通过key找到value,这里多说一句,无论是map还是set,他们的迭代器走的都是中序的顺序。
详解map、set、multimap、multiset的使用

map的使用
先说说两个注意点:

  1. map的迭代器访问这里有讲究,由于其迭代器指向的是由key和value组成的键值对,那* it该获得哪个key和value的哪个值呢?set的迭代器指向的就只有key,所以* it直接返回key值即可。
    对于map来说,*it拿到的是pair的对象,所以我们还需要再加一个.操作符才能访问pair对象里面的first和second值,但这样写起来有点麻烦,所以map的迭代器也重载了操作符,→重载的函数会返回迭代器指向对象的地址,所以还应该再加个→访问first或second才对,但是编译器在这里做了优化,省略了一个箭头。
  2. 在map这里,如果我们用语法糖遍历map时,最好采用const引用,因为map中存的是键值对pair,不用引用就会发生赋值,会调用pair的赋值重载函数,代价比较大,为了提升效率建议采用const引用。(语法糖遍历拿到的值其实就是*it,在map这里就是pair对象,键值对)

链接map
另外一种非常骚的操作就是利用map的[ ]来统计次数,在了解了insert的返回值之后,再来理解map的[ ]调用成本就会低很多了,实际上[ ]带来的作用包括插入,查找,修改的功能,直接一个[ ]运算符重载包揽map的三个重要功能。

详解map、set、multimap、multiset的使用
详解map、set、multimap、multiset的使用
对于map和set在插入后都会返回一个键值对:
键值对的first是迭代器,其指向的是新插入键值对,若新插入键值对的key已经存在,则返回已经存在的key对应的键值对的迭代器,这是first的变化规则。
键值对的second是bool值,如果插入的key已经存在,则bool为false,否则bool为true

详解map、set、multimap、multiset的使用
详解map、set、multimap、multiset的使用

map的使用方法如下:

  • 要使用map,需要添加map头文件,即#include<map>。除此之外,还需要在头文件下面加上一句:“using namespace std;”。
  • map的定义:可以使用map<键类型, 值类型> 变量名;来定义一个空的map对象,也可以使用map<键类型, 值类型> 变量名(初始值);来定义一个带有初始值的map对象。也可以指定一个比较函数对象来自定义键的排序规则。例如:
    • map<int, string> m1; // 定义一个空的map对象,存储int类型的键和string类型的值,按照默认的升序排列
    • map<int, string> m2({{1, "one"}, {2, "two"}}); // 定义一个带有初始值的map对象,存储int类型的键和string类型的值,按照默认的升序排列
    • map<int, string, greater<int>> m3; // 定义一个空的map对象,存储int类型的键和string类型的值,按照降序排列
  • map的插入:可以使用insert()方法或者emplace()方法(C++11)来向map中插入一个键值对,这两个方法都返回一个pair对象,其中first是一个指向插入位置的迭代器,second是一个布尔值,表示插入是否成功。如果插入的键已经存在于map中,则插入失败。也可以使用emplace_hint()方法(C++11)来向map中插入一个键值对,并提供一个提示位置作为参数,该方法返回一个指向插入位置的迭代器。如果提示位置是正确的或者接近正确的,则该方法比insert()或者emplace()更高效。还可以使用下标运算符[]或者at()方法来向map中插入或修改一个键值对,但这两种方式有所不同:下标运算符如果键不存在,则会创建一个新的键值对并返回默认值;而at()方法如果键不存在,则会抛出异常。例如:
    • m1.insert(pair<int, string>(1, "one")); // 向m1中插入{1, "one"}
    • m1.insert(make_pair(2, "two")); // 向m1中插入{2, "two"}
    • auto p = m1.insert({3, "three"}); // 向m1中插入{3, "three"},并获取返回值
    • cout << p.first->first << " " << p.first->second << endl; // 输出 3 three
    • cout << p.second << endl; // 输出 true
    • p = m1.insert({3, "san"}); // 尝试向m1中插入{3, "san"}
    • cout << p.second << endl; // 输出 false
    • m2.emplace(3, "three"); // 向m2中原位构造{3, "three"}
    • m2.emplace_hint(m2.begin(), 4, "four"); // 向m2中原位构造{4, "four"},并提供一个提示位置
    • m3[5] = "five"; // 向m3中插入或修改{5, "five"}
    • m3[6] = "six"; // 向m3中插入或修改{6, "six"}
    • cout << m3[7] << endl; // 输出 空字符串,并向m3中插入{7, ""}
    • cout << m3.at(8) << endl; // 抛出异常,并不会向m3中插入{8, ""}
  • map的删除:可以使用erase()方法来从map中删除一个键值对或者一个范围内的键值对,该方法返回一个整数,表示删除了多少个键值对。如果删除的键不存在于map中,则返回0。也可以使用clear()方法来清空map中的所有键值对。例如:
    • m1.erase(1); // 从m1中删除键为1的键值对
    • cout << m1.erase(4) << endl; // 输出 0,表示删除失败
    • auto it = m1.begin(); // 获取m1的起始迭代器
    • it++; // it指向第二个元素
    • m1.erase(it); // 从m1中删除it所指向的元素
    • it = m1.begin(); // 重新获取m1的起始迭代器
    • auto it2 = m1.end(); // 获取m1的结束迭代器
    • it2--; // it2指向最后一个元素
    • m1.erase(it, it2); // 从m1中删除[it, it2)范围内的元素
    • m2.clear(); // 清空m2中所有元素
  • map的查找:可以使用find()方法来查找map中是否存在某个键,并返回一个指向该键所在位置的迭代器,如果没有找到,则返回end()。也可以使用count()方法来统计map中某个键出现的次数,该方法返回一个整数,表示出现的次数(只能是0或者1)。还可以使用lower_bound()方法和upper_bound()方法来获取某个键在map中的边界位置,或者使用equal_range()方法来获取某个键在map中的范围。例如:
    • auto it = m2.find(3); // 查找m2中是否有键为3的元素
    • if (it != m2.end()) cout << it->first << " " << it->second << endl; // 如果找到了,则输出 3 three
    • cout << m2.count(4) << endl; // 输出 1,表示m2中有键为4的元素
    • cout << m2.count(5) << endl; // 输出 0,表示m2中没有键为5的元素
    • auto lb = m3.lower_bound(5); // 获取5在m3中的下界位置
    • auto ub = m3.upper_bound(5); // 获取5在m3中的上界位置
    • cout << lb->first << " " << lb->second << endl; // 输出 5 five
    • cout << ub->first << " " << ub->second << endl; // 输出 6 six
    • auto p = m3.equal_range(6); // 获取6在m3中的范围
    • cout << p.first->first << " " << p.first->second << endl; // 输出 6 six
    • cout << p.second->first << " " << p.second->second << endl; // 输出 7 空字符串

代码示例:

#include <iostream>
#include <map>
using namespace std;

int main() {
  map<int, string> m1; // 定义一个空的map对象,存储int类型的键和string类型的值,按照默认的升序排列
  map<int, string> m2({{1, "one"}, {2, "two"}}); // 定义一个带有初始值的map对象,存储int类型的键和string类型的值,按照默认的升序排列
  map<int, string, greater<int>> m3; // 定义一个空的map对象,存储int类型的键和string类型的值,按照降序排列

  m1.insert(pair<int, string>(1, "one")); // 向m1中插入{1, "one"}
  m1.insert(make_pair(2, "two")); // 向m1中插入{2,  "two"}); // 向m1中插入{2, "two"}
  auto p = m1.insert({3, "three"}); // 向m1中插入{3, "three"},并获取返回值
  cout << p.first->first << " " << p.first->second << endl; // 输出 3 three
  cout << p.second << endl; // 输出 true
  p = m1.insert({3, "san"}); // 尝试向m1中插入{3, "san"}
  cout << p.second << endl; // 输出 false
  m2.emplace(3, "three"); // 向m2中原位构造{3, "three"}
  m2.emplace_hint(m2.begin(), 4, "four"); // 向m2中原位构造{4, "four"},并提供一个提示位置
  m3[5] = "five"; // 向m3中插入或修改{5, "five"}
  m3[6] = "six"; // 向m3中插入或修改{6, "six"}
  cout << m3[7] << endl; // 输出 空字符串,并向m3中插入{7, ""}
  cout << m3.at(8) << endl; // 抛出异常,并不会向m3中插入{8, ""}

  m1.erase(1); // 从m1中删除键为1的键值对
  cout << m1.erase(4) << endl; // 输出 0,表示删除失败
  auto it = m1.begin(); // 获取m1的起始迭代器
  it++; // it指向第二个元素
  m1.erase(it); // 从m1中删除it所指向的元素
  it = m1.begin(); // 重新获取m1的起始迭代器
  auto it2 = m1.end(); // 获取m1的结束迭代器
  it2--; // it2指向最后一个元素
  m1.erase(it, it2); // 从m1中删除[it, it2)范围内的元素
  m2.clear(); // 清空m2中所有元素

  auto it = m2.find(3); // 查找m2中是否有键为3的元素
  if (it != m2.end()) cout << it->first << " " << it->second << endl; // 如果找到了,则输出 3 three
  cout << m2.count(4) << endl; // 输出 1,表示m2中有键为4的元素
  cout << m2.count(5) << endl; // 输出 0,表示m2中没有键为5的元素
  auto lb = m3.lower_bound(5); // 获取5在m3中的下界位置
  auto ub = m3.upper_bound(5); // 获取5在m3中的上界位置
  cout << lb->first << " " << lb->second << endl; // 输出 5 five
  cout << ub->first << " " << ub->second << endl; // 输出 6 six
  auto p = m3.equal_range(6); // 获取6在m3中的范围
  cout << p.first->first << " " << p.first->second << endl; // 输出 6 six
  cout << p.second->first << " " << p.second->second << endl; // 输出 7 空字符串

}

2.multimap

  • multimap允许键重复,而map不允许键重复,所以multimap是没有[ ]的。
  • multimap的插入操作总是成功,而map的插入操作可能失败,如果插入的键已经存在于map中。

multimap和map的其他用法和特点基本相同,都是基于红黑树实现的有序关联容器。

3.什么时候应该使用multimap而不是map

一般来说,应该使用multimap而不是map的情况是:

  • 需要存储多个具有相同键的键值对时,例如一个人的多个电话号码,一个词的多个同义词等。
  • 当不需要修改键值对的值时,因为multimap不支持使用下标运算符或者at()方法来修改值。
  • 不需要按照键的顺序遍历键值对时,因为multimap的迭代器可能会跳跃地访问相同键的元素。

最后两个OJ题作为练习
两个数组的交集
前K个高频单词文章来源地址https://www.toymoban.com/news/detail-436524.html

到了这里,关于详解map、set、multimap、multiset的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • map、multimap、set、multiset讲解

    2023年07月10日
    浏览(44)
  • 【高阶数据结构】map和set的介绍和使用 {关联式容器;键值对;map和set;multimap和multiset;OJ练习}

    关联式容器和序列式容器是C++ STL中的两种不同类型的容器。 关联式容器是基于键值对的容器 ,其中每个元素都有一个唯一的键值,可以通过键值来访问元素。关联式容器包括set、multiset、map和multimap。 序列式容器是基于元素序列的容器 ,其中元素按照一定的顺序排列,可以

    2024年02月11日
    浏览(41)
  • C++进阶:详细讲解容器set与map(pair、multiset、multimap)

    C++进阶:详细讲解容器set与map(pair、multiset、multimap) 上次介绍了搜索二叉树:C++进阶:二叉搜索树介绍、模拟实现(递归迭代两版本)及其应用 为了介绍后面的AVLTree和红黑树,我们要进行一些铺垫,就是set与map的介绍啦 关联式容器和序列式容器是 C++ 中两种不同的容器类

    2024年04月12日
    浏览(40)
  • 【C++】map/multimap/set/multiset的经典oj例题 [ 盘点&全面解析 ] (28)

    前言 大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《初学者易

    2024年02月04日
    浏览(45)
  • Google 开源库Guava详解(集合工具类)—Maps、Multisets、Multimaps

    Maps有许多很酷的实用程序,值得单独解释。 Maps.uniqueIndex(Iterable,Function)解决了一个常见的情况,即有一堆对象,每个对象都有一些唯一的属性,并希望能够根据该属性查找这些对象。 假设我们有一堆字符串,我们知道它们有唯一的长度,我们希望能够查找具有特定长度

    2024年02月03日
    浏览(45)
  • C++STL——map/multimap容器详解

    纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。 在STL中有些容器的元素是一种叫pair的数据结构。 对组(pair)是类模板,对组(pair)将一对值组合成一个值,一般用于表示key/value数据,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公

    2024年02月14日
    浏览(41)
  • 详解 C++中STL的map/multimap

    ap容器中所有元素都是pair,pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)。 同时,所有元素都会根据元素的键值自动排序。 map/multimap属于关联式容器,底层数据结构是用二叉树实现的。它的优点就是可以根据key值快速找到value值。 这里需要了解map与

    2024年02月16日
    浏览(49)
  • 【C++】详解map和set基本接口及使用

    关联式容器也是用来存储数据的,但与序列式容器不同的是,关联式容器里面存储的是 key, value 结构的键值对,因此**在数据检索时比序列式容器效率更高**。 键值对是用来表示 具有一一对应关系的一种结构 ,该结构中一般只包含两个成员变量 – key 和 value;其中 key 代表键

    2024年02月08日
    浏览(44)
  • C++STL详解(十) -- 使用哈希表封装unordered_set和unordered_map

    因为不同容器的类型不同,如果是unordered_map,V代表一个键值对,如果unordered_set,V代表Key值,而底层哈希表并不知道上层容器所要传递的V模板参数类型,为了与哈希表的模板参数区分,我们将哈希表中的V模板参数改为T. 例如: 在哈希表中当我们使用Find函数进行查找时: 如果上层传递的

    2024年02月01日
    浏览(56)
  • set/ multiset 容器

    简介: 所有元素都会在插入时自动被排序 本质: set/multiset属于 关联式容器 ,底层结构是用 二叉树 实现。 set和multiset区别 : set不允许容器中有重复的元素 multiset允许容器中有重复的元素 功能描述:创建set容器以及赋值 构造: setT st; //默认构造函数: set(const set st); //拷贝

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包