【STL】set、map的使用介绍

简介: 用来表示具有一一对应关系的一种数据结构,该结构中只包含两个成员变量key和value,key代表关键字,value表示关键字对应的值。比如:现在要建立一个英译汉的词典,那该词典中必然有英文单词和与其对应的中文含义,而且,英文单词与中文含义是一一对应的关系,

一. pair键值对


1. 概念


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

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

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


2. 构造方法


pair();

构造一个空的pair,其中first和second的值为对应类型的默认值

// 构造一个空的pair    
pair<int,int> p;    
cout<<p.first<<":"<<p.second<<endl;// 0:0

template<class k, class v> pair (const pair<k,v>& pr);

拷贝构造相同的pair,内部实现为浅拷贝

pair<int,int> p1;    
pair<int,int> p2(p1);// 用p1拷贝构造p2    
cout<<p2.first<<":"<<p2.second<<endl;// 0:0

pair (const first_type& a, const second_type& b);

最常用的构造方式,直接传first和second的值

pair<int,string> p(1,"hello world");
cout<<p.first<<":"<<p.second<<endl;// 1:hello world

make_pair构造一个临时对象

底层通过封装构造函数来实现,它可以自动识别我们传入数据的类型,通常用来构造临时对象作为参数传入。956ed1418e92417f8ae5daa4f808fd69.png



make_pair(1,"haha");    
make_pair(2,"hehe");


二. set


1. 概念


set是按照一定次序存储元素的容器,它的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序,在底层使用红黑树实现。在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素被const保护),但是可以从容器中插入或删除它们。

set的模板参数列表

2e387479bb8242d68ffd2eab909a26a4.png


  • T: set中存放元素的类型,实际在底层存储的是<value, value>键值对。
  • Compare:set中元素默认按照小于来比较。
  • Alloc:set中元素空间的管理方式,默认使用STL提供的空间配置器。


2. 补充说明


  • 头文件:#include< set >
  • set中插入元素时,只需要插入value即可,不需要构造键值对
  • set中的元素不可以重复(因此可以使用set进行去重)。
  • set中的元素不允许修改,因为这会打乱红黑树的结构,要修改的话只能先删除在插入。
  • 使用set的迭代器遍历set中的元素,可以得到有序序列,因为底层是红黑树
  • set中的元素默认按照小于来比较(即less< T >),若要更改比较方式第二个参数传入greater< T >即可。
  • set中查找某个元素,时间复杂度为logN


2. 使用介绍


2.1 set的构造

a1e45a7970c7433c820814b4d653d5b2.png


2.2 set增加元素 — insert


pair<iterator,bool> insert (const T& val)

功能:插入一个值为val的节点

返回值:返回一个pair<iterator, bool>的键值对。如果添加成功iterator就是这个元素的迭代器,bool为true。如果添加失败(已经存在该元素),iterator返回已经存在的那个元素的迭代器,bool为false。

参数:需要存储的key数据

样例:

set<int> s;  
s.insert(1);  
s.insert(3);  
s.insert(2);  
for(auto& e : s)  
{  
  cout<<e<<endl;  
}

编译运行:

e40ccd12c9314fba816ef01f259433da.png


2.3 set删除元素 — erase


void erase (iterator position);

功能:删除set中position迭代器位置上对应的节点

size_t erase (const T& val);

功能:删除值为val的节点

返回值:删除了的元素的数量

样例:

set<int> s;          
s.insert(1);         
s.insert(2);         
s.insert(3);         
for(auto& e : s)     
{                    
  cout<<e<<endl;     
}                    
// 删除值为3的节点
s.erase(3);
for(auto& e : s)
{
  cout<<e<<endl;
}

编译运行:

69a63302c86e45ea856bd3ffff189397.png


2.4 set查找元素 — find


iterator find (const T& val) const;

功能:查找值为val的节点

参数:要查找的节点的值

返回值:找到了返回节点的迭代器,找不到返回set::end,即最后一个节点的下一个位置的迭代器。


样例:

set<int> s;    
s.insert(1);    
s.insert(2);    
s.insert(3);    
// 查找3这个元素    
set<int>::iterator it = s.find(3);    
if(it!=s.end())    
{    
  cout<<*it<<endl;// 3
}                    

三. multiset


1. 介绍


multiset相当于set 2.0,只不过其中元素是可以重复的,multi这个前缀的意思“多”,底层实现也是红黑树。


2. multiset与set的区别


性质上的区别在与set不能存储值相同的元素,而multiset可以。

使用上的差别比如对于insert这个函数:set返回的是一个pair<iterator, bool>键值对,而multiset返回的是新插入节点的迭代器。


四. map


1. 概念


map是关联容器,它按照特定的次序(按照key来比较),存储由关键字key和值value组合而成的pair键值对。在map中,关键字key通常用于排序和惟一地标识元素,而值value中存储与此关键字key关联的内容。key和value的类型可能相同,也可能不同。

map的模板参数列表

2539f6c94eeb4726ad3ba7d3be0c4080.png

  • key: 键值对中key的类型
  • T: 键值对中value的类型
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(比如自定义类型),需要用户自己显式传递比较规则(仿函数类型)。
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器。


2. 补充说明


头文件:#include< map >

map支持下标访问符,即在[ ]中放入key,就可以找到与key对应的value

map的底层为红黑树


3. 使用介绍


3.1 map的构造


map()

功能:构造一个空的map

map<int double> m1;
map<double,int> m2;


3.2 map增加元素 — insert


pair<iterator,bool> insert (const pair<k,v>& val);

功能:插入一个键值对val

参数:要插入的pair键值对

返回值:返回一个pair<iterator, bool>的键值对。如果添加成功iterator就是这个元素的迭代器,bool为true。如果添加失败(已经存在该元素),iterator返回已经存在的那个元素的迭代器,bool为false。

样例:

// 演示insert    
string fruits[]={"西瓜","香蕉","苹果","苹果","西瓜","香蕉"};                                                 
map<string,int> m;    
// 统计各种水果的数量    
for(auto& e : fruits)    
{    
  pair<map<string,int>::iterator,bool> p = m.insert(make_pair(e,1));      
  if(p.second == false)      
  {      
    ++(p.first->second);      
  }      
}      
// 打印map      
for(auto& e : m)      
{      
  cout<<(e.first)<<":"<<(e.second)<<endl;      
}      

编译运行:

7c3d8660c8b846aaab9e16426cfa702c.png


3.3 map删除元素 — erase


size_t erase (const k& k);

功能:删除关键字key=k的节点

参数:要删除的关键字k

返回值:删除节点的个数

void erase (iterator position);

功能:通过迭代器来删除节点

参数:要删除节点的迭代器

返回值:无


3.4 map查找元素 — find


iterator find (const k& k);

const_iterator find (const k& k) const;

功能:查找一个值为k的节点

参数:需要查找的关键字k

返回值:如果找到了,返回该节点的迭代器;如果没找到返回最后一个节点的下一个位置的迭代器,即end()。

样例:

// 演示find
string fruits[]={"西瓜","香蕉","苹果","苹果","西瓜","香蕉"};
map<string,int> m;
// 统计各种水果的数量
for(auto& e : fruits)
{
  map<string,int>::iterator it = m.find(e);
  if(it!=m.end())
  {
    ++(it->second);
  }
  else 
  {
    m.insert(make_pair(e,1));
  }
}
// 打印map                                                                                                   
for(auto& e : m)      
{
  cout<<(e.first)<<":"<<(e.second)<<endl;
}

编译运行:

8b9818bfc95040c38ca4c0dffc085b1f.png


3.5 map修改元素 — [ ]


v& operator[ ] (const k& k);

功能


  • 查看:传入关键字k,就可以得到对应值的引用
  • 插入:传入关键字k,如果不存在的话就会插入pair(k,v())这个节点,并返回其值的引用;如果存在直接返回该节点的值的引用。
  • 修改:因为返回的是节点键值对的值value的引用,所以我们拿到后可以修改它。

PS:我们一般不用[ ] 来查看键值对的value,因为如果没有的话它会自动插入,造成不必要的麻烦。查看的话通常还是用find()。

底层原理

内部其实是对insert()进行了封装,为什么不用find()进行封装?因为find()如果查找失败只是返回一个没有作用的迭代器,自然也拿不到它的value,这与[ ]的特性不匹配(方括号的特性是无论如何都要拿到key所对应的value),而insert()不论插入成功亦或失败都可以节点的迭代器,继而拿到value。

// []底层其实是调用下面这个式子的
(*((this->insert(make_pair(k,mapped_type()))).first)).second

63f983bb88134b8b95824363e8bde080.png

样例:

// 演示[]                                                                                                    
string fruits[]={"西瓜","香蕉","苹果","苹果","西瓜","香蕉"};      
map<string,int> m;      
// 统计各种水果的数量      
for(auto& e : fruits)      
{      
  ++(m[e]);      
}      
// 打印map      
for(auto& e : m)      
{      
  cout<<(e.first)<<":"<<(e.second)<<endl;      
}  

编译运行:

84c28a91d95447aaaed03c019836b222.png


五. multimap


1. 介绍


multimaps也是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key, value>,其中多个键值对之间的key是可以重复的。在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容,底层实现也是红黑树。


2. multimap与map的区别


在性质上,前者可以存储相同key值的节点(至于value相同与否无所谓),而后者key必须唯一。

在使用上,区别比如insert():前者返回插入节点的迭代器,后者返回pair<iterator,bool>的键值对。另外就是前者没有重载方括号[ ];而后者重载了,因为map只有唯一的key。


相关文章
|
9天前
|
存储 JavaScript 索引
js开发:请解释什么是ES6的Map和Set,以及它们与普通对象和数组的区别。
ES6引入了Map和Set数据结构。Map的键可以是任意类型且有序,与对象的字符串或符号键不同;Set存储唯一值,无重复。两者皆可迭代,支持for...of循环。Map有get、set、has、delete等方法,Set有add、delete、has方法。示例展示了Map和Set的基本操作。
19 3
|
25天前
|
存储 数据格式
Set和Map的应用场景
Set和Map的应用场景
|
2月前
|
存储 自然语言处理 C++
map和set的简单介绍
map和set的简单介绍
21 1
|
2月前
|
存储 安全 Java
java集合框架及其特点(List、Set、Queue、Map)
java集合框架及其特点(List、Set、Queue、Map)
|
4月前
|
JavaScript 前端开发 定位技术
JavaScript 中如何代理 Set(集合) 和 Map(映射)
JavaScript 中如何代理 Set(集合) 和 Map(映射)
50 0
|
4月前
|
存储 安全 Java
Map和Set(JAVA)
Map和Set(JAVA)
50 1
|
4月前
|
编译器 C++ 容器
【C++学习手札】基于红黑树封装模拟实现map和set
【C++学习手札】基于红黑树封装模拟实现map和set
|
1天前
|
存储 前端开发 索引
【Web 前端】ES6中,Set和Map的区别 ?
【5月更文挑战第1天】【Web 前端】ES6中,Set和Map的区别 ?
|
9天前
|
存储 搜索推荐 C++
【C++高阶(二)】熟悉STL中的map和set --了解KV模型和pair结构
【C++高阶(二)】熟悉STL中的map和set --了解KV模型和pair结构
|
21天前
|
存储 C++ 容器
【C++初阶】STL详解(十)set、map、multiset、multimap的介绍及使用
【C++初阶】STL详解(十)set、map、multiset、multimap的介绍及使用
28 0