认识pair
在介绍容器之前,我们先来介绍一下pair,pair是一种结构体模板类型,每个pair可以存储两个值,这两个值的类型可以是任何类型的。它定义在 #include< uitility >中。在set map中使用pair的原因是让他们可以底层封装公用同一棵红黑树。
定义在 uitility文件中的pair 结构体模板部分代码。
template<class _Ty1,
class _Ty2>
struct pair
{ // store a pair of values
typedef pair<_Ty1, _Ty2> _Myt;
typedef _Ty1 first_type;
typedef _Ty2 second_type;
pair()
: first(), second()
{ // default construct
}
pair(const _Ty1& _Val1, const _Ty2& _Val2)
: first(_Val1), second(_Val2)
{ // construct from specified values
}
_Ty1 first; // the first stored value
_Ty2 second; // the second stored value
};
可以看到pair的两个成员分别是first和second。
关于set
set是一种key结构,它的元素就是它的键值,set不允许有两个相同的键值,set中的所有元素的键值都会自动被排序。set和multiset都包含在#include中。set和multiset的底层都是用红黑树实现的,不过set的底层插入机制是insert_unique,multiset的底层插入机制是insert_equal。
template< class Key,class Compare=less<Key>,class Allocator<Key> >
set也是一种模板,它的第一个模板参数是键值的类型。第二个参数是一个仿函数,它传入了键值的比较方式,决定是升序排序还是降序排序。第三个参数是空间配
置器。
set的作用操作
1、insert
1.1、pair< iterator,bool> insert(const value_type& x)
其中value_type是元素的类型在set中value_type 就是键值key,他会返回一个pair类型的对象,其中pair的second如果为true的话就表示插入成功,否则就表示元素已经存在。
first是一个指向要插入位置的迭代器。如果second是false,则frist就是已经存在的元素的这个位置的迭代器,如果second为true,first就是插入位置的迭代器。
1.2、iterator insert(iterator position,const valut_type& val)
position是一个迭代器的位置,val表示要插入的数。如果插入成功,则会返回一个新插入元素位置的迭代器。否则的话返回这个传入的迭代器。
1.3、template
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,itertor last)
删除一段迭代器区间。
void TestSet2()
{
//测试insert()
set<int> Myset3;
for (int i = 0; i < 5; ++i){
Myset3.insert(i * 10);
}
set<int>::iterator it3;
pair<set<int>::iterator, bool> ret = Myset3.insert(20);
if (ret.second == false){//insert()函数的返回值是pair<> pair<>的第一个模板参数记录插入位置或插入失败
it3 = ret.first; //位置的迭代器 第二个参数表示是否插入成功。
cout <<"插入失败"<< *it3 << endl;
}
int brr[] = { 1, 2, 3, 4 };
Myset3.insert(brr, brr + 4);//可以按区间插入
it3 = Myset3.begin();
while (it3 != Myset3.end()){
cout << *it3++ << " ";
}
cout << endl;
//测试删除操作
Myset3.erase(0);
it3 = Myset3.begin();
Myset3.erase(++it3);
it3 = Myset3.find(30);//查找值为30的所在位置返回该位置的迭代器。
Myset3.erase(it3, Myset3.end());//可以按区间删除
it3 = Myset3.begin();
while (it3 != Myset3.end()){
cout << *it3++ << " ";
}
cout << endl;
}
3.正反向迭代器 与size() empty()
size()返回容器中元素个数 empty()检查容器是否为空 空则返回true不空返回false。
void TestSet1()
{
//测试正反向迭代器
int arr[] = { 24, 53, 16, 97, 80, 98, 99 };
set<int> Myset(arr, arr + 7);
set<int>::iterator it = Myset.begin();//正向迭代器
while (it != Myset.end()){
cout << *it++ << " ";
}
cout << endl;
set<int>::reverse_iterator rit = Myset.rbegin();//反向迭代器
while (rit != Myset.rend()){
cout << *rit++ << " ";
}
cout << endl;
//测试 empty() size()
set<char> Myset2;
cout << "empty:" << Myset2.empty() << endl;
cout << "size:" << Myset2.size() << endl;
for (size_t i = 0; i < 5; ++i){
static char a = 'a';
Myset2.insert(a);
a++;
}
set<char>::iterator it2 = Myset2.begin();
while (it2 != Myset2.end()){
cout << *it2++ << " ";
}
cout << endl;
cout << "empty:" << Myset2.empty() << endl;
cout << "size:" << Myset2.size() << endl;
}
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表示存在。有木有感觉set中的count与empty的功能重复了,其实count在multiset中的作用比较大,但是为了统一STL中容器的接口,所以在set中也增加了count。
5.lower_bound(val): 返回容器中第一个值【大于或等于】val的元素的iterator位置。
upper_bound(val): 返回容器中第一个值【大于】val的元素的iterator位置。
6.比较操作 key_compare value_compare 是个仿函数 默认比较方式 <
void TestSet4()
{
int arr[] = { 44, 23, 12, 58, 69, 71, 37, 35 };
set<int> Myset6(arr, arr + 8);
cout << Myset6.count(12) << " " << Myset6.count(22) << endl;
set<int>::iterator itupper;
set<int>::iterator itlow;
itlow = Myset6.lower_bound(58);
itupper = Myset6.upper_bound(58);
cout << *itlow << " " << *itupper << endl;
set<int>::key_compare com;//默认升序。默认 <
set<int>::iterator it6 = Myset6.begin();
set<int>::reverse_iterator rit6 = Myset6.rbegin();
do{
cout << *it6 << " ";
} while (com(*it6++, *rit6));
cout << endl;
}
7.swap交换两个set clear()清空set
void TestSet3()
{
int arr[] = { 1, 2, 3, 4, 5, 6 };
int brr[] = { 7, 8, 9, 10, 11, 12 };
set<int> Myset4(arr, arr + 6);
set<int> Myset5(brr, brr + 6);
Myset4.swap(Myset5);//
set<int>::iterator it4 = Myset4.begin();
set<int>::iterator it5 = Myset5.begin();
while (it4 != Myset4.end()){
cout << *it4++ << " ";
}
cout << endl;
while (it5 != Myset5.end()){
cout << *it5++ << " ";
}
cout << endl;
Myset4.clear();
cout << "Myset4 size:" << Myset4.size() << endl;
while (it4 != Myset4.end()){
cout << *it4++ << " ";
}
cout << endl;
}
set的迭代器set::iterator是一种const_iterator,所以不允许通过迭代器修改set中的值。
multiset:
multiset的特性和用法和set的用法完全相同,唯一的差别就是它允许键值重复出现。下面可以下他们的不同之处:
1、insert
iterator insert(const value_type& val)如果直接插入一个键值的话,在multiset中返回的是插入元素位置的迭代器。
2、count
multiset中可以出现重复的键值。count统计的是相同键
值出现的次数。如果count的结果为0则表示没有出现,如果不为0则表示出现的次数 。
关于map
map是一种Key(键)、value(值)形式,用于保存键和值组成的条目集合。它要求键值必须是唯一的,值可以不唯一,同一个值可以映射到多个键上。所有的元素都会根据键值自动排序。map中的所有元素都是pair,同时拥有键值(Key)和实值(value),pair的first被视为键值,second被当做实值,但不允许有相同的键值。map的底层是用红黑树实现的,它的insert机制是一种insert_unique()。我们不可以通过迭代器修改元素的键值,但是可以通过迭代器修改元素的实值。
template< class Key,class T,class Compare=less,class Alloc=allocator< pair< const Key,T> > >
map也是一个模板,他有四个模板参数类型,第一个是键值的类型。第二个是实值的类型。第三个是一个仿函数,它传入的是键值的比较方式,默认是升序排列。第四个是空间配置器。
1、insert
1.1、pair< iterator,bool> insert(const value_type& val);
插入一个value_type类型,返回值是一个pair类型。 pair< iterator,bool>=pair< map< K,V>::iterator,bool>。如果插入成功的话则bool就为true,且iterator指向插入的位置,否则的话iterator就指向已经存在的这个元素的位置。iterator是一个pair< K,V>类型的迭代器。
1.2、iterator insert(iterator,const value_type& val)
在一个迭代器的位置插入一个value_type类型的数据,如果插入成功的话返回这个成功位置的迭代器,如果失败的话则返回这个传入的迭代器。
1.3、template
void insert(InputIterator first,InputIterator last);
插入一段迭代器区间
2、erase
2.1、void erase(iterator position)
删除一个这个迭代器位置的元素。
2.2、size_type erase(const key_type& k)
删除这个键值所在的位置,成功返回1,失败返回0
2.3、void erase(iterator first,iterator last);
删除一段迭代器区间。
void TestMap3()
{
map<string, string> mp;
mp.insert(make_pair("left", "左边"));
mp.insert(make_pair("right", "右边"));
mp.insert(make_pair("up", "上"));
mp.insert(make_pair("down", "下"));
mp.insert(make_pair("tree", "树"));
map<string, string>::iterator it1 = mp.begin();
cout << "插入之后的map:";
while (it1 != mp.end())
{
cout << it1->first << " ";
++it1;
}
cout << endl;
mp.erase("left");
it1 = mp.begin();
mp.erase(++it1, mp.end());
it1 = mp.begin();//防止迭代器失效
cout << "删除之后的map:";
while (it1 != mp.end())
{
cout << it1->first << " ";
++it1;
}
cout << endl;
}
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中有这个k,则它就把这个k所对应的value的引用返回。如果map中没有这个k的话,则它会调用insert(pair< K,V>(k,V())),将k和V的缺省值对应起来插入进去,并返回这个value的引用。
这个接口经常会在map中用到,甚至比直接调用insert还好用,因此重点介绍一下这个接口。
T& operator[] ( const key_type& x );
这里的T其实也是一个V类型,返回一个V的引用,operator[]实际上是这样实现的。
V& operator[](const K&k)
{
return (*((this->insert(make_pair(k, mapped_type()))).first)).second;
}
operator[] () 调用了insert()函数 insert()函数的返回值是pair
在map中 这个返回的pair的first是指向map元素(实际上也是一个pair)的迭代器 这个pair的second是实值value。返回他。
所以operator[]既可以用来插入也可以通过key用来修改实质。
void TestMap2()
{
map<string, string> m1;
m1["hehe"] = "呵呵";
m1["up"] = "上";
m1["down"] = "下";
cout << m1.size() << endl;
cout << m1.empty() << endl;
map<string, string>::iterator it1 = m1.begin();
map<string, string>::reverse_iterator rit1 = m1.rbegin();
while (it1 != m1.end()){
cout << (it1++)->second << " ";
}
cout << endl;
while (rit1 != m1.rend()){
cout << (rit1++)->second << " ";
}
cout << endl;
m1["up"] = "upon";//修改K值为"up"的V值为"upon";
it1 = m1.begin();
while (it1 != m1.end()){
cout << (it1++)->second << " ";
}
cout << endl;
map<string, string>::iterator it = m1.upper_bound("hehe");
cout << it->first << endl;
it = m1.lower_bound("hehe");
cout << it->first << endl;
}
multimap:
multimap与map的特性及用法完全相同,唯一的差别是multimap允许键值重复。multimap包含在#include
3、multimap中没有operator[],这个很好理解,当你调用operator[]的时候会对应多个结果,返回哪个都不合适,所以也就没有operator[]。
其他操作与set基本相同。