C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也称为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。
因map中的数据都是以pair形式出现的键-值对,所以叫做映射;而set中的数据本身既是键值也是实值,所以叫做集合。
注意:除了允许元素重复之外,multiset的定义形式和set是相同的。本文以set为主进行说明。
Set是一种随机存取得容器,其关键词和数据文件是同一个值。Set对象中不能包含重复元素,集合set型容器能顺序存储一组值,在一个集合中,集合元素既充当存储的数据,又充当数据的关键值。集合本质是一个有序的链表,元素以升序的顺序存储。
template<class Key,class Traits=less<key>,class Allocator=allocator<Key>> class set
其中,参数Key是存在在容器中的数据类型;参数Traits是用于实现集合内部排序的仿函数,默认参数为less;参数allocator代表集合内存配置器,负责内存的分配和销毁。
① 定义一个空对象,参数包含了一个谓词,用于容器内部排序
explicit set(const traits& _Comp);
② 包含一个谓词之外,还加入了内存配置器
explicit set(const Traits& _Comp,const Allocator& _Al);
③ 使用已存在的集合(set)定义新的集合
set(const _set& _Right);
④ 使用迭代器指定固定的范围,用于定义新的集合(set)
template<class InputIterator> set(InputIterator _First,InputIterator _Last);
⑤ 比第4种形式增加参数_Comp,从而可以自定义排序准则
template<class InputIterator> set(InputIterator _First,InputIterator _Last,const traits& _Comp);
⑥ 在5的基础上增加内存配置器的内容
template<class InputIterator> set(InputIterator _First,InputIterator _Last,const traits& _Comp,const Allocator& _Al);
常见创建方法:
创建一个空set
set<int>s1;
创建一个带大于比较器的set,默认是小于比较器less<int>
set<int,greater<int> > s22;
用拷贝构造函数初始化set
set<int> s3(s1);
用数组初始化set
int a[4] = {1,2,3};
set<int> s4(a,a+3);
区间初始化
set<int> s5(s1.begin(),s1.end());
size_type size() const;
bool empty() const;
//返回容器最大容量
size_type max_size() const;
//count(Key)的功能是统计键值为Key的元素个数
size_type count(const Key& _Key)const;
//find(Key)的功能是返回键值为Key的元素的位置,函数返回值是迭代器类型
iterator find(const Key& _Key)const;
const_iterator find(const Key& _Key)const;
//lower_bound(const Key& _Key)的返回值类型是迭代器类型,该迭代器指向集合中键值大于等于Key的第一个元素,如果没有找到返回迭代器末尾位置。
const_iterator lower_bound(const Key& _Key)const;
//upper_bound(const Key& _Key)的返回值类型是迭代器类型,该迭代器指向集合中键值大于Key的第一个元素,如果没有找到返回迭代器末尾位置。
const_iterator upper_bound(const Key& _Key)const;
iterator upper_bound(const Key& _Key)const;
//函数eqaul_range (const Key& _Key)const的返回值类型是迭代器对(pair),分别指向集合中键值大于等于Key的第一个元素和集合中键值大于Key的第一个元素
pair <const_iterator,const_iterator>eqaul_range (const Key& _Key)const;
pair <iterator,iterator>eqaul_range (const Key& _Key)const;
学习代码:
#include
#include
#include
using namespace std;
void Print(const int &Ele)
{
cout << Ele << ",";
}
int main()
{
typedef set<int> SET;
typedef multiset<int> MSET;
SET s1;
MSET s2;
SET::iterator it;
MSET::iterator Mit;
pair<SET::iterator, SET::iterator> p1;
pair<MSET::iterator, MSET::iterator> Mp1;
s1.insert(10);
s1.insert(15);
s1.insert(21);
s1.insert(17);
s2.insert(16);
s2.insert(20);
s2.insert(18);
s2.insert(20);
cout << "The Set S1 is below:" << endl;
for_each(s1.begin(), s1.end(), Print);
cout << endl;
cout << "The Multiset S2 is below:" << endl;
for_each(s2.begin(), s2.end(), Print);
cout << endl;
//计算容量
int size = s1.size();
int Msize = s2.size();
cout << "The size of the set s1:" << size << endl;
cout << "The size of the Multiset s2:" << Msize << endl;
//计算最大容量
int max_size = s1.max_size();
int max_Msize = s2.max_size();
cout << "The max_size of the set s1:" << max_size << endl;
cout << "The max_size of the Mset s2:" << max_Msize << endl;
//指定元素个数
int cnt = s1.count(10);
int Mcnt = s2.count(20);
cout << "The count of \'10\' in set s1:" << cnt << endl;
cout << "The count of \'20\' in multiset s2:" << Mcnt << endl;
//查找元素
it = s1.find(21);
Mit = s2.find(18);
cout << "The element with a key '21' is:" << *it << endl;
cout << "The element with a key '18' is:" << *Mit << endl;
//查找元素
it = s1.lower_bound(15);
Mit = s2.lower_bound(16);
if (it == s1.end())
{
cout << "The element with a key 15 in s1 doesn't exit" << endl;
}
else
{
cout << "The element with a key 15 in s1 is: " << *it << endl;
}
if (Mit == s2.end())
{
cout << "The element with a key 15 in s2 doesn't exit" << endl;
}
else
{
cout << "The element with a key 16 in s2 is: " << *Mit << endl;
}
//upper_bound
it = s1.upper_bound(15);
Mit = s2.upper_bound(15);
if (it == s1.end())
{
cout << "The element with a key >15 in s1 doesn't exits." << endl;
}
else
{
cout << "The element with a key >15 in s1 is:" << *it << endl;
}
if (Mit == s2.end())
{
cout << "The element with a key >15 in s2 doesn't exits." << endl;
}
else
{
cout << "The element with a key >16 in s2 is:" << *Mit << endl;
}
//equal_range
p1 = s1.equal_range(15);
Mp1 = s2.equal_range(16);
if (it == s1.end())
{
cout << "The element with a key >= 15 in s1 doesn't exits;" << endl;
}
else
{
cout << "The element with a key = 15 in s1 is:" << *p1.first << endl;
cout << "The element with a key > 15 in s1 is:" << *p1.second << endl;
}
if (Mit == s2.end())
{
cout << "The element with a key >= 16 in s2 doesn't exits;" << endl;
}
else
{
cout << "The element with a key = 16 in s2 is:" << *Mp1.first << endl;
cout << "The element with a key > 16 in s2 is:" << *Mp1.second << endl;
}
return 0;
}
2.4.2 迭代器相关函数和赋值函数
Set和multiset提供了所有容器提供的基本赋值操作:“=“,swap()。赋值操作两端容器必须具有相同的类型。
Set和multiset相关的迭代器函数主要包括:begin(),end(),rbegin(),rend()
其中begin()和end()是双向迭代器,rbegin()和rend()是逆向迭代器,对于迭代器,所有的元素都被视为常数。
学习代码:
//交换函数swap
cout << "交换s1和s2"<< endl;
s1.swap(s2);
cout << "s1:" << endl;
for_each(s1.begin(),s1.end(),Print);
cout << endl;
cout << "s2:" << endl;
for_each(s2.begin(),s2.end(),Print);
cout << endl;
set<double>::iterator its;
set<double>::reverse_iterator rits;
its = s1.begin(); //第一个元素
cout << "The first Element of sequence \'s1\':" << *its << endl;
its = s1.end(); //第二个元素
cout << "The last Element of sequence \'s1\':" << *its << endl;
rits = s1.rbegin(); //逆向第一个元素
cout << "The first Element of sequence in reverse direction .\'s1\':" << *rits << endl;
rits = s1.rend();
cout << "The last Element of sequence in reverse direction .\'s1\':" << *rits << endl;
2.4.3 插入和删除
函数原型如下:
//返回值为pair类型,pair的第一个参数代表元素插入的位置,第二个参数为安插是否成功
pair<iterator,bool> insert(const value_type& x);
//函数的第一个参数指定插入的位置,第二个为插入的值
iterator insert(iterator it,const value_type &x);
//插入一个区间
void insert(const value_type *first,const value_type *last);
//函数功能为将迭代器所指向的元素删除
iterator erase(iterator it);
//将迭代器所指范围内的元素全部删除
iterator erase(iterator first,iterator last);
//将元素Key删除
size_type erase(const Key& key);
学习代码:
#include
#include
#include
using namespace std;
void Print(const int & Ele)
{
cout << Ele << " ";
}
template <typename T>
void printSet(set<T>& s)
{
for_each(s.begin(),s.end(),Print);
cout << endl;
}
int main()
{
set<int>s1,s2;
pair<set<int>::iterator,bool> p1;
s1.insert(10);
s1.insert(11);
s1.insert(13);
s1.insert(21);
s1.insert(17);
cout << "s1:" << endl;
printSet(s1);
p1 = s1.insert(12);
if(p1.second == true)
{
cout << "The element 12 be inserted successfully" << endl;
}
else
{
cout <<"The element 12 already exit in s1." << endl;
}
cout << "s1:" << endl;
printSet(s1);
p1 = s1.insert(17);
if(p1.second == true)
{
cout << "The element 17 be inserted successfully" << endl;
cout << "The position of 17 is:" << distance(s1.begin(),p1.first)+1 << endl;
}
else
{
cout << "The element 17 already exist in s1." << endl;
cout << "The position of 17 is:" << distance(s1.begin(),p1.first)+1 << endl;
}
cout << "s1:" << endl;
printSet(s1);
//赋值给s2
s2 = s1;
s2.insert(25);
cout << "s2:" << endl;
printSet(s2);
s2.erase(25);
cout << "s2:(after erasing 25.)" << endl;
printSet(s2);
s2.erase(s2.begin());
cout << "s2:(after erasing *begin())" << endl;
printSet(s2);
s2.erase((++s2.begin()),(--s2.end()));
cout << "s2:(after erasing from ++begin() to --end())" << endl;
printSet(s2);
return 0;
}
2.4.4 比较运算符
①Key_comp()用于键值比较。
Key_compare key_comp() const;
②函数value_comp()用于实值比较。
Value_compare value_comp() const;
函数key_compare()的返回值是key_compare类型,key_compare决定了集合中元素的排序顺序,通过对返回值的测试,可以确定序列的排序形式。函数value_comp()的返回值是value_compare类型,其意义和key_compare一致。
学习代码:
#include
#include
#include
using namespace std;
void Print(const int &Ele)
{
cout << Ele << " ";
}
template <typename T>
void printSet(set<T> &s)
{
for_each(s.begin(),s.end(),Print);
cout << endl;
}
template <typename T>
void printMSet(multiset<T,greater<T> > &s)
{
for_each(s.begin(),s.end(),Print);
cout << endl;
}
int main()
{
set<double,less<double> > s1;
s1.insert(1);
s1.insert(5);
s1.insert(7);
s1.insert(4);
multiset<double,greater<double> > s2;
s2.insert(7);
s2.insert(9);
s2.insert(6);
s2.insert(4);
set<double,less<double> > ::key_compare kc1 = s1.key_comp();
multiset<double,greater<double> >::key_compare kc2 = s2.key_comp();
cout << "s1:" << endl;
printSet(s1);
cout << "s2:" << endl;
printMSet(s2);
if(kc1(2,3) == true)
{
cout << "The function object of s1 return true." << endl;
}
else
{
cout << "The function object of s1 return false." << endl;
}
if(kc2(2,3) == true)
{
cout << "The function object of s2 return true." << endl;
}
else
{
cout << "The function object of s2 return false." << endl;
}
return 0;
}
注意:map和multimap基本一致,只是multimap允许重复元素,而map不允许。
①容器map是键-值对的集合,map由许多对的键值组成的排序结构体。键值key通常用于唯一的标识元素,而值value中存储与此键值key关联的内容;
②键值key和value的类型可能相同也可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名为pair。
③元素的顺序仅由key决定,和value无关。默认排序准则为“less<>”
④map的底层实现为红黑树,查找效率比较高,是O(logN)。
容器map和multimap的构造函数:
map(); //默认构造函数
map(const map &m); //拷贝构造函数
map(iterator begin,iterator end); //区间构造函数
map(iterator begin,iterator end,const traits& _compare); //带比较谓词的构造函数
map(iterator begin,iterator end,const traits& _compare,const allocator& all); //带分配器
map和multimap的形式:
对于map/multimap的定义,可以是由以上两种表的任意组合。
例如:
map
multimap
map
(注意:greater后面的‘>’与map的后面‘>’的要空一个空格,不然会报错)
、、、、、、
代码示例:
提示:map根据key键进行排序
#include
#include
using namespace std;
typedef pair<int, string> custompair;
int main()
{
map<int, string> mapstudent;
mapstudent.insert(custompair(1, "john"));
mapstudent.insert(custompair(3, "lisa"));
mapstudent.insert(custompair(7, "bob"));
map<int, string>::iterator iter;
for (iter = mapstudent.begin(); iter != mapstudent.end(); iter++)
{
cout << iter->first << " " << iter->second << endl;
}
map<int, string, greater<int>> mapstudentG;
mapstudentG.insert(custompair(1, "john"));
mapstudentG.insert(custompair(3, "lisa"));
mapstudentG.insert(custompair(7, "bob"));
map<int, string, greater<int>>::iterator iterG;
for (iterG = mapstudentG.begin(); iterG != mapstudentG.end(); iterG++)
{
cout << iterG->first << " " << iterG->second << endl;
}
return 0;
}
3.3.1 容量、是否为空、迭代器
size()返回现有大小;
max_size()返回最大容量;
调用empty(),当map或者multimap为空时,返回true;
begin()与end()为正向迭代器,rbegin()与rend()为反向迭代器;
局部示例代码:
//直接调用
int size = m1.size();
int m_size = m1.max_size();
bool isempty = m1.empty();
map<int,double,greater<int> > m;
multimap<int,double,greater<int> > itM;
itM = m.begin();
cout << itM->first << " " << itM->second << endl;
itM = m.rbegin();
cout << itM->first << " " << itM->second << endl;
//将输出结果进行比较既可以
3.3.2 插入与删除
容器的insert()函数可以加入一个对象、一个对象的若干赋值,或者某个范围内的对象。函数insert()把一个或若干个元素插入iterator指示的位置,所插入的元素将出现在iterator指出的位置之前。
1. 函数insert原型:
//将元素x插入map的末尾
pair<iterator,bool> insert(const value_type& x);
//将元素x插入迭代器it之前
iterator insert(iterator it,const value_type &x);
//将迭代器(first,last)指定范围的元素插入map中
void insert(const value_type *first,const value_type *last);
函数插入方法:
①用insert函数插入pair数据
map
mapStudent.insert(pair
②调用make_pair()产生一个pair数据
mapStudent.insert(make_pair(2,“student_two”));
③insert()方法中显式指定数据类型的数据如下插入法:
mapStudent.insert(map
在调用map成员方法向map对象中插入数据时,为了防止出现数据的隐式转换,可以在insert()方法中使用map容器的value_type表示被插入的数据类型。
value_type定义在见map模板的黑体字部分:
template
typename _Alloc=std::allocator
class map
{
public:
typedef _Key Key_type;
typedef _Tp mapped_type;
typedef std::pair
typedef _compare key_compare;
typedef _Alloc allocator_type;
…
}
④以下标方式[]插入
mapStudent[4] = “student_four”
2 函数erase()原型:
//函数功能为将迭代器所指向的元素删除
iterator erase(iterator it);
//将迭代器所指范围内的元素全部删除
iterator erase(iterator first,iterator last);
//将元素Key删除
size_type erase(const Key& key);
函数clear()原型:
void clear() const;
函数clear() 可以删除容器中的所有元素,并且比函数erase()的管理功能更强大而且更加灵活。
函数clear()等效于erase(map::begin(),map::end());
3.3.3 元素个数统计、查找元素和元素的随机访问
1.cout()
对于map容器来说,只存在0和1的结果,因为map不能存在重复
对于multimap容器来说,可以返回元素重复的个数。
2.find()
iterator find(const Key & key);
const_iterator find(const Key& key)const;
返回指向Key的迭代器,如果没有返回end();
还可以使用STL的通用算法find()和find_if()。这两个函数可以使用迭代器iterator。通常第一个迭代器指向开始的位置,第二个迭代器指向停止处理的地方。并且第二个迭代器指向的元素不被处理。对于multimap型容器,其中存在多个相同的元素,在使用find()函数,返回第一个出现查找到元素的迭代器。
3.upper_bound()和lower_bound()与equal_range()
equal_range()实现的功能是返回返回容器中元素的上下限的两个迭代器,第一个迭代器和函数lower_bound()返回值一样,第二个迭代器和函数upper_bound()返回值一样。如果元素没找到就返回end()。
局部代码:
typedef pair<int,double> mypair;
map<int,double,greater<int> >::iterator it1;
multimap<int,double,greater<int> >::iterator it2;
typedef map<int,double,greater<int> >Map;
typedef multimap<int,double,greater<int> >MMap;
Map m1;
MMap m2;
mypair temp;
pair<Map::iterator,Map::iterator> p1;
pair<MMap::iterator,MMap::iterator> p2;
//此处可以通过insert函数插入数据
m1.insert(mypair(1,5.7));
m1.insert(mypair(2,8.2));
m1.insert(mypair(3,9.5));
m1.insert(mypair(4,10.5));
m1.insert(mypair(5,5.7));
m2.insert(mypair(1,5.7));
m2.insert(mypair(2,8.2));
m2.insert(mypair(2,9.5));
m2.insert(mypair(4,10.5));
m2.insert(mypair(4,4.5));
......
//find()函数
it1 = m1.find(5);
temp = *it1;
cout << temp.first << " " << temp.second << endl;
p1 = m1.equal_range(3);
cout << "The lower_bound<3> and The upper_bound<3>:" << p1.first->second << " " << p1.second->second << endl;
p2 = m2.equal_range(4);
cout << "The lower_bound<4> and The upper_bound<4>:" << p2.first->second << " " << p2.second->second << endl;
4.内存分配器
用到的函数为get_allocator(),返回map/multimap的内存配置器。
什么是内存配置器?
它类似于指针的首地址,指明对象的初始存储位置。
typedef map<int,double> Map;
Map::allocator_type m1_alloc;
Map::allocator_type m2_alloc;
Map::allocator_type m3_alloc;
Map::allocator_type m4_alloc;
Map m1,m2,m3;
m1_alloc = m1.get_allocator();
m2_alloc = m2.get_allocator();
m3_alloc = m3.get_allocator();
Map m4(less<int> (),m1_alloc);
m4_alloc = m4.get_allocator();
if(m1_alloc == m4_alloc)
{
cout << "配置器相同" << endl;
}
else
{
cout << "配置器不同" << endl;
}
输出结果:配置器相同
5.比较运算符
①键值比较key_comp();
typedef map<int,double> Map;
Map m1;
Map::key_compare kc1 = m1.key_comp();
if(kc1(2,3))
{
cout << "true" << endl;
}
else
{
cout << "false" << endl;
}
②实值比较value_comp();
#include
#include
using namespace std;
typedef map<int, double, less<int> > Map;
typedef multimap<int, double, greater<int> > MMap;
typedef pair<Map::iterator, bool> mypair;
int main()
{
Map m1;
MMap m2;
MMap::iterator itA, itB;
mypair p1, p2;
Map::value_compare vc1 = m1.value_comp();
MMap::value_compare vc2 = m2.value_comp();
p1 = m1.insert(map<int, double>::value_type(2, 11));
p2 = m1.insert(map<int, double>::value_type(9, 85));
if (vc1(*p1.first, *p2.first) == true)
{
cout << "元素(2,11)在元素(9,85)之前" << endl;
}
else
{
cout << "元素(2,11)在元素(9,85)之后" << endl;
}
itA = m2.insert(map<int, double>::value_type(2, 11));
itB = m2.insert(map<int, double>::value_type(9, 85));
if (vc2(*itA, *itB))
{
cout << "元素(2,11)在元素(9,85)之前" << endl;
}
else
{
cout << "元素(2,11)在元素(9,85)之后" << endl;
}
return 0;
}
《C++ STL 标准程序库开发指南》–闫常友 王敏
《C++泛型STL原理和应用》 --任哲 房红征 张永忠
本次写作看着知识点不多,但是却耗费了很多时间与精力。
真正体会到了写一本书不是一两周的事情,感谢这些作者的辛勤劳作,将知识传递给更多人!
有错误欢迎在评论区指出,大家一起进步。