初学者:set/multiset&&map/multimap

set/multiset && map/multimap

一、概要

初学者:set/multiset&&map/multimap_第1张图片
C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也称为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。
因map中的数据都是以pair形式出现的键-值对,所以叫做映射;而set中的数据本身既是键值也是实值,所以叫做集合。

二、set/multiset

注意:除了允许元素重复之外,multiset的定义形式和set是相同的。本文以set为主进行说明。

2.1 什么是set?

Set是一种随机存取得容器,其关键词和数据文件是同一个值。Set对象中不能包含重复元素,集合set型容器能顺序存储一组值,在一个集合中,集合元素既充当存储的数据,又充当数据的关键值。集合本质是一个有序的链表,元素以升序的顺序存储。

2.2 set的声明

template<class Key,class Traits=less<key>,class Allocator=allocator<Key>> class set

其中,参数Key是存在在容器中的数据类型;参数Traits是用于实现集合内部排序的仿函数,默认参数为less;参数allocator代表集合内存配置器,负责内存的分配和销毁。

2.3 几种集合set的创建方法

① 定义一个空对象,参数包含了一个谓词,用于容器内部排序

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());

2.4 set/multiset提供的函数

初学者:set/multiset&&map/multimap_第2张图片
2.4.1 容量、搜寻和统计

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

注意:map和multimap基本一致,只是multimap允许重复元素,而map不允许。

3.1 简介

①容器map是键-值对的集合,map由许多对的键值组成的排序结构体。键值key通常用于唯一的标识元素,而值value中存储与此键值key关联的内容;

②键值key和value的类型可能相同也可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名为pair。

③元素的顺序仅由key决定,和value无关。默认排序准则为“less<>”

④map的底层实现为红黑树,查找效率比较高,是O(logN)。

3.2 map的定义与初始化

容器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); //带分配器

初学者:set/multiset&&map/multimap_第3张图片
map和multimap的形式:
初学者:set/multiset&&map/multimap_第4张图片
对于map/multimap的定义,可以是由以上两种表的任意组合。
例如:
map c;
multimap c(begin,end);
map c(begin,end);
(注意: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 map/multimap提供的函数

初学者:set/multiset&&map/multimap_第5张图片
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;
mapStudent.insert(pair(1,“student_one”));
②调用make_pair()产生一个pair数据
mapStudent.insert(make_pair(2,“student_two”));
③insert()方法中显式指定数据类型的数据如下插入法:
mapStudent.insert(map::value_type(3,“student_three”));

在调用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 value_type;
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原理和应用》 --任哲 房红征 张永忠
本次写作看着知识点不多,但是却耗费了很多时间与精力。
真正体会到了写一本书不是一两周的事情,感谢这些作者的辛勤劳作,将知识传递给更多人!
有错误欢迎在评论区指出,大家一起进步。

你可能感兴趣的:(容器,c++,数据结构)