容器——map和set

目录

    • 关联式容器
    • 键值对
        • 概念
        • 接口介绍
            • 类原型
            • 成员变量
            • 构造
            • 赋值
            • 交换
            • 创建
    • `set`
        • 概念介绍
        • 接口介绍
            • 类原型
            • 构造函数
            • 迭代器
            • 容量操作
            • 插入
            • 删除
            • 交换
            • 其他
            • 查找
    • `map`
        • 概念介绍
        • 接口介绍
            • 类原型
            • 构造函数
            • 迭代器
            • 容量操作
            • 访问操作
            • 插入操作
            • 删除
            • 交换
            • 其他
            • 查找
    • `multiset`
        • 介绍
        • 接口
    • `multimap`
        • 介绍
        • 接口
    • 注意
        • `set`注意点
        • `map`注意点

关联式容器

  • 序列式容器:在前面我们学习了 STL 中的stringvectorlistdeque等容器,而这些容器因为其底层是线性序列的数据结构,里面存储的是元素本身,所以我们将它们称之为序列式容器;
  • 关联式容器:关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的不单单是数据本身,而是存储了关键码和数据的结构的键值对,该种容器在数据检索时比序列式容器效率更高;

键值对

概念

  • 概念:用来表示具有一 一对应关系的一种结构,该结构中一般只包含两个成员变量 key 和 value,key 代表关键码,value 表示与 key 相对应的信息,比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词以及与英文单词对应的中文含义,而且英文单词与其中文含义是一 一对应的关系,即通过该英文单词,在词典中就可以找到与其对应的中文含义。
  • 下面是 STL 中对于键值对的定义:
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)
	{}
};

接口介绍

类原型
template <class T1, class T2> 
struct pair;
成员变量
  • pair对象中存在两个成员变量:
pair<int, int> p(2, 5);
p.first:获取键值对中的第一个数据的值;
p.second:获取键值对中的第二个数据的值;
构造
  • pair<数据类型1, 数据类型2> p;:构造空的键值对;
  • pair<数据类型1, 数据类型2> p(数据1, 数据2);:构造含有关键码和数据值的键值对;
  • pair<数据类型1, 数据类型2> p(同类型的键值对);:用已有键值对构造一个键值对,这两个键值对数据类型要保持一致;
赋值
  • pair& operator= (const pair& pr);:将一个已有键值对的值赋给另一个已有键值对,这两个键值对数据类型要保持一致;
交换
  • void swap (pair& pr);:交换两个键值对的关键码和数据值,这两个键值对数据类型要保持一致;
创建
  • 函数模板:使用make_pair函数可以为空的键值对创建出一个键值对对象;
template <class T1, class T2>
pair<T1,T2> make_pair(T1 x, T2 y);

pair<int, int> p;
p = make_pair(2, 4);

set

概念介绍

  1. set是按照一定次序存储键值对的容器;
  2. set中,元素的 value 和 key 是相同的 (value == key,类型都为 T),因此每个 value 必须是唯一的,并且set中的元素不能在容器中修改 (元素总是const的),但是可以从容器中插入或删除它们;
  3. 在内部,set中的元素总是按照其内部比较对象 (所存储的数据类型的比较规则) 所指示的特定严格弱排序准则进行排序;
  4. set容器通过 key 访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代;
  5. set在底层是用二叉搜索树 (红黑树) 实现的;
  6. 在使用set时需要包含头文件

接口介绍

类原型
template < class T,                    //set存放的元素类型中元素
           class Compare = less<T>,    //比较规则,less升序,greater降序
           class Alloc = allocator<T>  //set中元素空间的管理方式
           > 
class set;
  • compare:比较器的类型,set中的元素是按照 value 来比较的,缺省情况下按照小于来比较,一般情况下 (内置类型元素) 该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则,一般情况下有两种传递方式:函数指针、仿函数;
bool fncomp (int lhs, int rhs) {return lhs<rhs;}

struct classcomp {
  bool operator() (const int& lhs, const int& rhs) const
  {return lhs<rhs;}
};
set<int,classcomp> fifth;                

bool(*fn_pt)(int,int) = fncomp;
set<int,bool(*)(int,int)> sixth (fn_pt);
构造函数
  • set<数据类型> s;:构造空的set对象;
  • set<数据类型> s(const set& x);:用一个构造好的set对象拷贝构建一个set对象,数据类型要保持一致;
  • 函数模板:使用 [first, last) 区间中的元素构造一个set对象,数据类型保持一致;
template<class T>
set<数据类型> s(T first, T last);
  • 初始化列表:创建set对象的另一种方式是使用初始化列表来指定初始值以及元素个数,可以用等号后面加花括号来输入参数,也可以直接在变量名后加花括号来输入参数,如果是自定义类型,那么输入的参数则是构造函数(参数)的形式;
struct A{
	A(int a = 1, int b = 2)
		:_a(a)
		,_b(b)
	{}
	int _a;
	int _b;
};
//内置类型
set<int> s1 = {1, 2, 3};
set<int> s2{1, 2, 3};
//自定义类型
set<A> s3 = {A(1, 2), A(3, 4)};
set<A> s4{A(1, 2), A(3, 4)};
迭代器

  set容器的迭代器在使用上和string容器的迭代器是基本一样的,只需将string中的每个字符替换为set中的每个元素即可,因此就不做过多介绍,这里附上string容器的迭代器的介绍以及set容器的迭代器的网上详细定义;

  • 简单介绍
  • 详细定义
  • 注意
    1. 由于set中的元素关键码和数据值相同,因此不允许直接修改,所以迭代器中的const类迭代器与非const类迭代器的使用是一样的:只可读,不可写;
    2. 由于set容器底部的结构是和二叉搜索树类似的红黑树,因此如果使用迭代器对set对象进行遍历的话,那么将会有序的将对象中的数据打印出来;
容量操作
  • bool empty ( ) const;:检测set是否为空,为空返回 true,否则返回 false;
  • size_type size() const;:返回set中有效元素的个数;
  • size_type max_size() const;:返回可以使用的最大空间,这和底层的空间适配器有关;
插入
  • pair insert(const value_type& x);:在set中插入元素 x,实际插入的是 构成的键值对,如果插入成功,返回一个pair类型的键值对:<该元素在set中的迭代器位置, true>,如果插入失败,说明 x 在set中已经存在,返回一个pair类型的键值对:
  • iterator insert (iterator position, const value_type& val);:在指定迭代器的位置插入数据 val,但是该迭代器位置只是一个建议,底层听不听从得看插入位置是否满足set的大小关系性质,所以最终插入位置不定,成功则返回插入后的位置,失败则说明 val 在set中已经存在,所以返回 val 在set中存在的迭代器位置;
  • 函数模板:将迭代器区间的元素插入set对象中;
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
删除
  • void erase (iterator position);:删除指定迭代器位置的元素,迭代器位置不能传入非法值;
  • size_type erase (const value_type& val);:删除指定数据的结点,返回值代表了删除了多少个等于 val 的值,一般为 0 或 1,0 代表删除失败,1 代表删除成功;
  • void erase (iterator first, iterator last);:删除指定迭代器区间中的元素,迭代器位置不能传入非法值;
交换
  • void swap (set& x);:交换两个set对象的数据;
其他
  • void clear();:清空set对象的内容;
  • 函数模板:尾插一个数据,如果是自定义类型数据,那么只需传入构造函数所需参数即可,返回值与insert函数一样;
template <class... Args>
pair<iterator,bool> emplace (Args&&... args);
  • 函数模板:在迭代器位置插入一个数据,如果是自定义类型数据,那么只需传入构造函数所需参数即可,返回值与insert函数一样;
template <class... Args>
iterator emplace_hint (const_iterator position, Args&&... args);
查找
  • iterator find (const value_type& val);:查找指定数据 val 是否存在,如果存在则返回数据的迭代器,如果不存在则返回end迭代器;
  • size_type count (const value_type& val) const;:查找指定数据 val 在set对象中的个数,因为set的关键码与数据值相同,所以数据不会重复出现,所以返回值只能为 1 或 0;

map

概念介绍

  1. map是关联容器,它按照特定的次序 (按照 key 来比较) 存储由键值 key 和值 value 组合而成的键值对;
  2. map中,键值 key 通常用于排序和唯一地标识元素,而数据值 value 中存储与对应键值 key 关联的内容,键值 key 和数据值 value 的类型可以不同,并且 value 值可以重复;
  3. map内部中的元素总是按照键值 key 进行比较排序的;
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代 (即对map中的元素进行迭代时,可以得到一个有序的序列);
  5. map支持下标访问符,即在[]中放入 key,就可以找到与 key 对应的 value;
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树));
  7. 在使用map时需要包含头文件;

接口介绍

类原型
template < class Key,                                   // key数据类型
           class T,                                     // val数据类型
           class Compare = less<Key>,                   // 比较规则,与set类似
           class Alloc = allocator<pair<const Key,T> >  // 底层容器分配
           > 
class map;
  • compare:比较器的类型,map中的元素是按照 key 来比较的,缺省情况下按照小于来比较,一般情况下 (内置类型元素) 该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则,一般情况下有两种传递方式:函数指针、仿函数;
bool fncomp (char lhs, char rhs) {return lhs<rhs;}

struct classcomp {
  bool operator() (const char& lhs, const char& rhs) const
  {return lhs<rhs;}
};
map<char,int,classcomp> fourth;         
bool(*fn_pt)(char,char) = fncomp;
map<char,int,bool(*)(char,char)> fifth (fn_pt);
构造函数
  • map<关键码类型, 数据值类型> m;构造空的map对象;
  • map<关键码类型, 数据值类型> m(const map& mp);:使用map对象来拷贝构造出一个map对象,数据类型要保持一致;
  • 函数模板:使用迭代区间中的内容构造一个map对象,注意数据类型要保持一致;
template<class T>
map<关键码类型, 数据值类型> m(T frist, T second);
bool fncomp (char lhs, char rhs) {return lhs<rhs;}

struct classcomp {
  bool operator() (const char& lhs, const char& rhs) const
  {return lhs<rhs;}
};

int main (){
  map<char,int> first;
  first['a']=10;
  first['b']=30;
  first['c']=50;
  first['d']=70;
  map<char,int> second (first.begin(),first.end());
  map<char,int> third (second);
  map<char,int,classcomp> fourth;                 // class as Compare
  map<char,int,bool(*)(char,char)> fifth (fncomp); // function pointer as Compare
  return 0;
}
  • 初始化列表:创建map对象的另一种方式是使用初始化列表来指定初始值以及元素个数,可以用等号后面加花括号来输入参数,也可以直接在变量名后加花括号来输入参数,如果是自定义类型,那么输入的参数则是构造函数(参数)的形式;
struct A {
	A(int a = 1, int b = 2)
		:_a(a)
		, _b(b)
	{}
	int _a;
	int _b;
};
//内置类型
map<int, int> m1 = { {1, 2}, {2, 3}, {3, 9}, {4, 2} };
map<int, int> m2{ {1, 2}, {2, 3}, {3, 9}, {4, 2} };
//自定义类型
map<A, int> m3 = { {A(1, 2), 4}, {A(3, 4), 5}, {A(4, 2), 4} };
map<A, int> m4{ {A(1, 2), 4}, {A(3, 4), 5}, {A(4, 2), 4} };
迭代器

  map容器的迭代器在使用上和string容器的迭代器是基本一样的,只需将string中的每个字符替换为map中的每个元素即可,因此就不做过多介绍,这里附上string容器的迭代器的介绍以及map容器的迭代器的网上详细定义;

  • 简单介绍
  • 详细定义
  • 注意
    1. 由于map中 key 是唯一的,而 value 并不是唯一的,因此 value 值可以重复存储,并且可以对非const的迭代器的second成员变量进行修改,而const的迭代器仍不可以修改;
    2. 虽然map容器底部的结构是和二叉搜索树类似的红黑树,但是如果使用迭代器对map对象进行遍历的话,那么只会将键值 key 有序的打印出来,而 value 的顺序未知;
容量操作
  • bool empty ( ) const;:检测map是否为空,为空返回 true,否则返回 false;
  • size_type size() const;:返回map中有效元素的个数;
  • size_type max_size() const;:返回可以使用的最大空间,这和底层的空间适配器有关;
访问操作
  • mapped_type& at (const key_type& k);:对于map对象,可以访问已存在的 key 值所对应的 value 值,并且可以修改已存在的 key 值所对应的 value 值,而对于不存在的 key 值的访问会抛异常;
  • mapped_type& operator[] (const key_type& k);:对于map对象,可以访问已存在的 key 值所对应的 value 值,并且可以修改已存在的 key 值所对应的 value 值,而对于不存在的 key 值的访问则会创建一个map元素,并将其 value 值设置成你所要修改的值(如果只是访问而没有修改,则会设置为默认值),然后插入其中;
  • []的访问操作的原理:[]操作其实等价于(*((this->insert(make_pair(k,mapped_type()))).first)).second
    容器——map和set_第1张图片
插入操作
  • pair insert (const value_type& val);:插入一个pair键值对,键值对的表示方法有两种pair(key, value)或者make_pair(key, value),如果插入成功,返回一个pair类型的键值对:<该元素在map中的迭代器位置, true>,如果插入失败,说明 key 在map中已经存在,返回一个pair类型的键值对:
  • iterator insert (iterator position, const value_type& val);:在指定迭代器位置插入一个pair键值对,方式有两种,同上,成功则返回插入后的位置,失败则说明 key 在map中已经存在,所以返回 key 在map中存在的迭代器位置;
  • 函数模板:将迭代区间中的元素插入到map对象中;
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
删除
  • void erase (iterator position);:删除指定迭代器位置的元素,迭代器位置不能传入非法值;
  • size_type erase (const key_type& k);:删除指定 key 值的结点,返回值代表了删除了多少个 key 值等于 k 的元素,一般为 0 或 1,0 代表删除失败,1 代表删除成功;
  • void erase (iterator first, iterator last);:删除指定迭代器区间中的元素,迭代器位置不能传入非法值;
交换
  • void swap (set& x);:交换两个map对象的数据;
其他
  • void clear();:清空map对象的内容;
  • 函数模板:尾插一个数据,如果是自定义类型数据,那么只需传入构造函数所需参数即可,返回值与insert函数一样;
template <class... Args>
pair<iterator,bool> emplace (Args&&... args);
  • 函数模板:在迭代器位置插入一个数据,如果是自定义类型数据,那么只需传入构造函数所需参数即可,返回值与insert函数一样;
template <class... Args>
iterator emplace_hint (const_iterator position, Args&&... args);
查找
  • iterator find (const key_type& k);:查找指定关键码 key 是否存在,如果存在则返回该键值对的迭代器,如果不存在则返回end迭代器;
  • size_type count (const key_type& k) const;:查找指定关键码 key 在map对象中的个数,因为map的关键码 key 不会重复出现,所以返回值只能为 1 或 0;

multiset

介绍

  1. multiset是按照特定顺序存储元素的容器,其中元素是可以重复的;
  2. multiset中,元素的 value 也可以用来识别它 (因为multiset中本身存储的就是 组成的键值对,因此 value 本身就是 key,key 就是 value,类型为 T),multiset元素的值不能在容器中进行修改 (因为元素总是const的),但可以从容器中插入或删除。
  3. 在内部,multiset中的元素总是按照其内部比较规则 (数据类型的比较规则) 所指示的特定严格弱排序准则进行排序;
  4. multiset容器通过 key 访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列;
  5. multiset底层结构为二叉搜索树(红黑树);

接口

  • multisetset的使用基本没有什么区别,只是multiset中可以存储相同的元素而已;

multimap

介绍

  1. Multimaps是关联式容器,它按照特定的顺序,存储由 key 和 value 映射成的键值对 ,其中多个键值对之间的 key 是可以重复的;
  2. multimap中,通常按照 key 排序并且按照 key 唯一地标识元素,而 value 存储与 key 关联的内容,key 和 value 的类型可能不同;
  3. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对 key 进行排序的;
  4. multimap通过 key 访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于 key 有序的序列;
  5. multimap在底层用二叉搜索树(红黑树)来实现;

接口

  • multimapmap的使用基本相似,有两点不同:
    1. multimap中的 key 是可以重复的;
    2. multimap中没有重载operator[]操作,因为 key 值不唯一,所以会产生二义性;

注意

set注意点

  1. setmap / multimap不同,map / multimap中存储的是真正的键值对 set中只放 value,但在底层实际存放的是由 构成的键值对;
  2. set中插入元素时,只需要插入 value 即可,不需要构造键值对;
  3. set中的元素不可以重复 (因此可以使用set进行去重),所以set重元素不能修改;
  4. 使用set的迭代器遍历set中的元素,可以得到有序序列;
  5. set中的元素默认按照小于来比较;
  6. set中查找某个元素,时间复杂度为:log2n;

map注意点

  1. map中的的元素是键值对;
  2. map中的 key 是唯一的,并且不能修改;
  3. 默认按照小于的方式对 key 进行比较;
  4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列;
  5. map中查找某个元素,时间复杂度为:log2n;
  6. 支持[]操作符,operator[]中实际进行创建插入查找;

你可能感兴趣的:(知识总结——C++进阶,c++,数据结构,容器,map,set)