在STL中map和set都是关联式容器,vector和list是序列式容器,在今天的这篇文章中主要介绍的是map和set的基本用法。
一、set
set是一种key类型的集合结构,所以set是不允许有重复元素的一种结构,set中所有元素的key值都会被自动排序为升序。set和multiset都包含在头文件#include<set>
中,set和multiset的底层都是用红黑树实现的,但是set的底层插入机制时insert_unique
,multiset的底层插入机制时insert_equal
。
template < class Key, class Compare = less<Key>,
class Allocator = allocator<Key> > class set;
由上述定义可知,set也是一种模板,它的第一个参数Key代表的是键值的类型;第二个参数是一个仿函数,它传入的是键值的比较方式,决定其是升序还是降序;第三个参数是空间配置器。我们主要关心的是set的第一个参数。
set的常用操作:
在介绍set和map的常用操作之前,先来介绍一种模板类型pair,每个pair可以存储两个值,这两个值可以是任意类型的,pair的两个成员分别是first和second,且pair包含在头文件#include<utility>
中。
1、insert,set的插入有三种类型
1.1、pair<iterator,bool> insert(const value_type& x)
其中的value_type是键值的类型,这个版本的插入会返回一个pair类型的对象,其中pair对second成员为false说明插入失败,该元素已经存在,反之则说明插入成功。pair的first成员指向的是要插入位置的迭代器,如果second是false,则first就是这个已经存在元素的迭代器;如果second成员是true,则first就会指向插入位置的迭代器。
1.2、iterator insert(iterator position,const valut_type& val)
postion的类型是一个迭代器的位置,vla表示要插入的数据。如果插入成功,则会返回一个新插入位置的迭代器,否则的话返回这个传入的迭代器。
1.3、插入一段迭代器区间
template<class InputIterator>
void insert(InputIterator first,InputIterator last)
2、erase,删除
2.1、void erase(iterator position)
删除一个迭代器位置
2.2、size_type erase(const value_type& val)
删除成功返回1,删除失败返回0
2.3、void erase(iterator first,iterator last)
删除从first到last的一段迭代器空间
3、find,查找
iterator find(const value_type& val) const;
如果找到的话就返回这个位置的迭代器,否则就返回end
4、count,统计键值出现的次数
size_type count(const value_type& val) const;
由于set里面不允许有重复出现的键值,所以count的返回结果就只有0和1两个结果,0就表示不存在,1表示存在。这个功能不就和empty功能重复了吗?当然不会啦!count这个接口一般在multiset中使用的最多,这个主要是为了统一接口罢了。
5、set的迭代器set::iterator是一种const_iterator,所以不允许通过迭代器修改set中的值。
当然了,set中的接口还有很多,在这里只介绍这几个。
multiset的常用操作:
multiset的特性和用法和set的用法完全相同,唯一的差别就是multiset是允许键值重复出现的。
1、insert,插入
iterator insert(const value_type& val)
如果直接插入一个键值的话,在multiset中返回的是插入元素位置的迭代器。
2、count
multiset中可以出现重复的键值。count统计的是相同键值出现的次数。如果count的结果为0则表示没有出现,如果不为0则表示出现的次数 。
下面是我写的一个关于set操作的简单例子:
void TestSet()
{
//set是K类型的
set<string> Myset;
Myset.insert("left");
Myset.insert("right");
Myset.insert("high");
Myset.insert("low");
Myset.insert("right");
Myset.insert("right");
cout<<"size?"<<Myset.size()<<endl; //4
cout<<"max_size?"<<Myset.max_size()<<endl;
set<string>::iterator it=Myset.begin();
while(it != Myset.end())
{
cout<<*it<<" ";
++it;
}
cout<<endl;
cout<<Myset.count("right")<<endl; //1 统计right出现的次数,set是去重的
it=Myset.find("left");
if(it != Myset.end()) //找到了
{
cout<<*it<<endl; //left
}
//Myset.erase(it); //删除一个迭代器
Myset.erase(it,Myset.end()); //删除两个迭代器之间的范围
}
二、map
map是一种Key(键)、value(值)形式的结构,用于保存键和值组成的条目集合。它要求键值必须是唯一的,值可以不唯一,同一个值可以映射到多个键上(所以map在vlaue层面上是允许重复的,这一点不同于set)。所有的元素都会根据键值自动排序。map中的所有元素都是pair,同时拥有键值(Key)和实值(value),pair的first被视为键值,second被当做实值。我们是不可以修改map的key值的,但是我们可以更改map的vlaue值。map的底层是用红黑树实现的,它的insert机制是一种insert_unique()
。
template<class Key,class T,class Compare=less<Key>,class Alloc=allocator<pair<const Key,T> > >
map也是一个模板,他有四个模板参数类型,第一个是键值的类型(key);第二个是实值的类型(value);第三个是一个仿函数,它传入的是键值的比较方式,默认是升序排列;第四个是空间配置器。
map的常用操作:
1、insert,插入
1.1、pair<iterator,bool> insert(const value_type& val);
插入一个value_type类型,返回值是一个pair类型。 pair
template<class InputIterator>
void insert(InputIterator first,InputIterator last);
2、erase
2.1、void erase(iterator position)
删除的是position迭代器位置的元素。
2.2、size_type erase(const key_type& k)
删除这个键值所在的位置,成功返回1,失败返回0。
2.3、void erase(iterator first,iterator last);
删除一段从first到last的迭代器区间
3、find
iterator find(const key_type& k)
如果查找成功的话则返回这个元素的迭代器,否则返回end,所以在使用find返回的迭代器之前要先判断一下。
4、count
size_type count(const key_type& k)const
map中的count与set的count功能一样。
5、operator[]
mapped_type& operator[](const key_type& k)
map中的operator[]是最常用的,map中operator[]的底层实现利用的是insert来实现的。
multimap:
multimap与map的特性及用法完全相同,但是multimap是允许键值重复的。multimap和 map都包含在头文件#include<map>
中,它们的底层也是用红黑树实现的,它的insert机制是insert_equal
。它的count与multiset的count的用法及作用完全一样。下面看一下他们的不同:
1、insert
iterator insert(const value_type& val)
如果插入一个元素的话,multimap会返回一个这个插入位置的迭代器。
2、count
count与multiset的count完全相同,multimap中的count统计的是键值出现的次数。
3、multimap中没有重载operator[]。
简单的例子测试map的基本操作:利用map实现一个字典树,利用map统计出现次数最多的前k个水果。
//template<class K,class V> //自己编写的一个简单的pair
//struct pair
//{
// K first;
// V second;
// pair(const K& key,const V& value)
// :first(key)
// ,second(value)
// {}
//};
//构造一个pair的对象返回
template<class K,class V>
pair<string,int> make_pair(const K& key,const V& value)
{
return pair<string,int>(key,value);
}
void TestDict()
{
//map是K,V类型的
map<string,string> dict;
dict.insert(pair<string,string>("value","值"));
dict.insert(pair<string,string>("left","左边"));
dict.insert(pair<string,string>("right","右边"));
dict.insert(pair<string,string>("up","上"));
dict.insert(pair<string,string>("down","下"));
dict["left"]="剩余"; //也可以使用operator[]来插入或者修改数据的value
map<string,string>::iterator it=dict.begin();
while(it != dict.end())
{
cout<<(*it).first<<":"<<(*it).second<<endl;
++it;
}
cout<<endl;
}
vector<int> GetTopKFruits(const vector<string>& fruits)
{
typedef map<string,int> Map;
typedef map<string,int>::iterator MapIt;
vector<int> v;
//map<string,int> countMap;
Map countMap;
for(size_t i=0;i<fruits.size();i++)
{
//方法一,find和insert都遍历了这个map,效率不行,但是方便理解,简洁直观
//map<string,int>::iterator it=countMap.find(fruits[i]);
//if(it != countMap.end()) //找到了
//{
// it->second++;
//}
//else //没有找到则插入到map中
//{
// //countMap.insert(pair<string,int>(fruits[i],1));
// countMap.insert(make_pair(fruits[i],1));
//}
//方法二,只查找一次,利用返回pair类型的insert函数
//pair<MapIt,bool> res=countMap.insert(make_pair(fruits[i],1));
//if(res.second == false) //找到了,插入失败
//{
// res.first->second++;
//}
//方法三,利用operator[]来实现,operator[]的底层是利用insert来实现的,具体可参考前面提到的operator[]的实现原理
countMap[fruits[i]]++;
}
map<string,int>::iterator it=countMap.begin();
while(it != countMap.end())
{
v.push_back(it->second);
++it;
}
return v;
}
void TestFruits()
{
vector<string> fruits;
fruits.push_back("菠萝");
fruits.push_back("梨");
fruits.push_back("水蜜桃");
fruits.push_back("梨");
fruits.push_back("菠萝");
fruits.push_back("水蜜桃");
fruits.push_back("香蕉");
fruits.push_back("菠萝");
fruits.push_back("梨");
fruits.push_back("水蜜桃");
fruits.push_back("梨");
fruits.push_back("菠萝");
fruits.push_back("水蜜桃");
fruits.push_back("香蕉");
vector<int> res=GetTopKFruits(fruits);
for(size_t i=0;i<res.size();i++)
{
cout<<res[i]<<" ";
}
cout<<endl; // 4 4 4 2
}
在这里就分享结束了~~~