您的位置:首页 > 编程语言 > C语言/C++

C++ 标准模板库(STL)_有序关联容器—— map + multimap(侯捷老师)

2020-07-15 04:43 337 查看

STL—— map + multimap

  • 1.2、 multimap定义
  • 1.3、 map结构部分源码
  • 1.3、 multimap结构部分源码
  • 1.4、 map[]操作符(set、multimap没有)
  • 1.4、 map常见操作
  • 参考
  • map + multimap

    1、map + multimap定义


    注意map的key的属性是const

    1.1、 map定义

    #include<map>
    • map的每一个元素都拥有两个值,一个键值(key)和一个实值(value)。可理解为“{键,值}对”。例如,学生的“学号”与“姓名”就可以用map进行描述。
    • 它的内部实现是用一个pair来保存这个两个值。所以,map的每一个元素又是一个pair。

      (与set相同,map同样是以红黑树RB_Tree为底层机制的关联式容器,所以具有和Set类似的特征,这里不赘述)

    1.1.1、 map特征

    • 1、每个元素的key值都唯一,Key - value的一 一对应
    • 2、元素的值自动进行排序(key用于rb_tree排序),默认比较键的函数是 less(),pair<key, value>才是rb_tree的节点
    • 插入、删除、查找 O(log2n)(面试常问)
    • 3、元素的key值不能直接被改变(key为const类型),value可以被改变
    typedef std::pair<const _Key, _Tp>                    value_type;

    1.2、 multimap定义

    multimap 是关联容器的一种,multimap 的每个元素都分为关键字和值两部分,容器中的元素是按关键字排序的,并且允许有多个元素的关键字相同。注意,不能直接修改 multimap 容器中的关键字。

    • map/mulitmap不一样的是,map插入的时候调用的是insert_unique(),而multimap调用的是insert_equal()。

    1.3、 map结构部分源码

    template <class Key, class T, class Compare = less<Key>, class Alloc
    class map {
    public:
    typedef Key key_type;//用于re_tree排序
    typedef T data_type;
    typedef T mapped_type;
    typedef pair<const Key, T> value_type; // 在rb_tree中value的类型是pair,rb_tree节点
    typedef Compare key_compare;
    private:
    // select1st直接return T.first 用于rb_tree取到key进行比较大小
    typedef rb_tree<key_type, value_type,
    select1st<value_type>, key_compare, Alloc> rep_type;
    rep_type t; // map的成员变量,red-black tree representing map
    // ...
    
    // 接口只是对rb_tree的封装  同set
    iterator begin() { return t.begin(); }
    iterator end() { return t.end(); }
    pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); }
    
    public:
    //再对类型重命名
    typedef typename rep_type::pointer pointer;
    typedef typename rep_type::const_pointer const_pointer;
    typedef typename rep_type::reference reference;
    typedef typename rep_type::const_reference const_reference;
    typedef typename rep_type::iterator iterator;
    typedef typename rep_type::const_iterator const_iterator;
    typedef typename rep_type::reverse_iterator reverse_iterator;
    typedef typename rep_type::const_reverse_iterator const_reverse_iterator;
    typedef typename rep_type::size_type size_type;
    typedef typename rep_type::difference_type difference_type;
    
    // ...
    }

    上述默认的仿函数为

    _Select1st

    template<typename _Pair>
    struct _Select1st
    : public unary_function<_Pair, typename _Pair::first_type>
    {
    typename _Pair::first_type&
    operator()(_Pair& __x) const
    { return __x.first; }
    };//operator()

    节点pair结构

    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) {}
    #ifdef __STL_MEMBER_TEMPLATES
    template <class U1, class U2>
    //pair的first是一个迭代器
    pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
    #endif
    };

    参考

    1.3、 multimap结构部分源码

    同map一样multimap不允许修改key。但是插入使用的是_M_insert_equal

    template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
    class multimap {
    public:
    //下面的定义与map相同
    typedef _Key                 key_type;
    typedef _Tp                  data_type;
    typedef _Tp                  mapped_type;
    typedef pair<const _Key, _Tp> value_type;
    typedef _Compare             key_compare;
    
    //嵌套类,提供键值key比较函数接口
    //继承自<stl_function.h>中的binary_function
    /*
    template<class _Arg1, class _Arg2, class _Result>
    structbinary_function {
    typedef_Arg1 first_argument_type;
    typedef_Arg2 second_argument_type;
    typedef_Result result_type;
    };
    */
    private:
    //底层机制是RB-Tree
    typedef _Rb_tree<key_type, value_type,
    _Select1st<value_type>,key_compare, _Alloc> _Rep_type;
    _Rep_type _M_t;  // red-black treerepresenting multimap
    public:
    typedef typename _Rep_type::pointer pointer;
    typedef typename _Rep_type::const_pointer const_pointer;
    typedef typename _Rep_type::reference reference;
    typedef typename _Rep_type::const_reference const_reference;
    //map的迭代器不直接定义为const_iterator,而是分别定义iterator,const_iterator
    //是因为map的键值key不能被修改,因为必须遵守比较函数的排序规则,所以必须定义为const_iterator
    //而map的实值value可以被修改,则定义为iterator
    typedef typename _Rep_type::iterator iterator;
    typedef typename _Rep_type::const_iterator const_iterator;
    typedef typename _Rep_type::reverse_iterator reverse_iterator;
    typedef typename _Rep_type::const_reverse_iteratorconst_reverse_iterator;
    typedef typename _Rep_type::size_type size_type;
    typedef typename _Rep_type::difference_type difference_type;
    typedef typename _Rep_type::allocator_type allocator_type;
    
    // allocation/deallocation
    // 注意:multimap只能使用RB-tree的insert-equal(),不能使用insert-unique()
    
    class value_compare : public binary_function<value_type, value_type,bool> {
    friend class multimap<_Key,_Tp,_Compare,_Alloc>;
    protected:
    _Compare comp;
    value_compare(_Compare __c) : comp(__c) {}
    public:
    bool operator()(const value_type& __x, const value_type& __y)const {
    return comp(__x.first, __y.first);
    }
    };

    1.4、 map[]操作符(set、multimap没有)


    作用:

    • 1.读功能。它的参数是一个key值,返回值是一个T的引用。T就是实值(value)的类型。我们利用它的返回值可以读到key值为x的元素的value。
    • 2.写功能。写入一个新元素
    //map的[]操作符返回传递给map的第二个参数。传递给[]一个key,如果查找到,就是value,否则就是默认值0。
    mapped_type&
    operator[](const key_type& __k)
    {
    iterator __i = lower_bound(__k);// 找到__k第一个。
    // __i->first is greater than or equivalent to __k.
    if (__i == end() || key_comp()(__k, (*__i).first))
    #if __cplusplus >= 201103L
    __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,
    std::tuple<const key_type&>(__k),
    std::tuple<>());
    #else
    __i = insert(__i, value_type(__k, mapped_type()));//重点在这
    #endif
    return (*__i).second;  // 返回key的value,此value为传递进map的第二个参数。
    }

    先搞清楚下面几点。

    • ①巧妙的点就在于它的源码实现,它在里面调用了一个insert。它将insert的返回值得first解引用,并返回解引用后的到值得second。
    • ②从上面我们知道insert的返回值是一个pair。pair的first是一个迭代器。当插入成功时,迭代器指向新插入元素。当插入失败时,迭代器指向已存在元素位置。
    • ③map的元素是pair类型。pair的second就是value。

    分析:

    • ①operator[]的参数是一个key值。它将这个key值做为insert的参数,调用insert函数。并得到一个返回值。如果参数key不存在于map中,insert就会插入成功,返回一个pair<Iterator,bool>,这个pair的迭代器就是指向新插入元素的位置。
    • ②再对这个返回值是一个pair<Iterator,bool>类型的值
    pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); }
    /*返回值为一个pair。pair的first是一个迭代器,second是一个bool值。当插入成功时,返回值pair的迭代器指向插入元素的位置,bool值为true。插入失败时(说明该元素已经存在),迭代器指向已存在元素的位置,bool值为false。*/

    这个pair的first是一个迭代器,注意这个迭代器的指向(细看insert第一种实现),对这个迭代器解引用又得到一个pair<key,value>类型的值。并返回它的second(一个value值)。

    所以说,当key在map中存在时,operator完成的是读操作。当key不存在是,完成一个写操作。写入一个新的pair<key,T()>。

    1.4、 map常见操作

    1.4.1、定义和赋值

    //定义,typename1, typename2代表不同的数据类型。
    map<typename1, typename2> mp;
    
    //插入和赋值
    1、mp.insert(map<typename1, typename2>::value_type(keys,value));
    2、mp.insert(pair<typename1, typename2>(keys,value));
    3、 mp[1] = value;

    注意:

    • 1、如果map中存在相同的key,使用insert插入方法不能改变关键字对应的值,插入无效。
    • 2、使用数组的赋值方式就可以覆盖原来关键字对应的值
    map.insert(map<typename1, typename2>::value_type (key1,value1));
    
    map.insert(map<typename1, typename2>::value_type (key1,value2));//key相同,此次执行无效
    #include<iostream>
    #include<string>
    #include<vector>
    #include<map>using namespace std;
    int main() {
    map<int, int> m;//定义map
    
    m[1] = 1;
    m[1] = 2;//第3种插入方式 使用下标插入
    //key不存在时创建 存在时对原有覆盖
    pair<int, int> p1;//定义键值对
    p1 = make_pair(2, 7);
    
    pair<int, int> p2(2, 4);//直接构造键值对
    
    m.insert(p1);
    m.insert(p2);//第2种插入方式 使用insert接口
    //key不存在时创建 存在时放弃,这句不起作用
    m.insert(map<int, int>::value_type(3,3));//第1种插入方式
    m.insert(pair<int, int>(4, 4));
    cout << m[2]<<endl;
    
    map<int, int>::iterator itr;
    for (itr = m.begin(); itr != m.end(); itr++) {  //遍历
    cout << itr->first << " " << itr->second << endl;
    }
    return 0;
    }
    

    1.4.2、访问和查找

    访问:

    • 1、通过下标访问
    • 2、通过迭代器访问 正向迭代器
    • 反向迭代器
    map<typename1, typename2>::iterator itr;//正向迭代器
    
    map<typename1, typename2>::reverse_iterator itr;//正向迭代器

    查找1:

    • 1、使用
      map.count(key)
      ,查找某个关键字是否在map中出现,返回1(存在)或者0(不存在)
    • 2、使用
      map.find(key)
      ,返回一个迭代器,如果存在,迭代器指向数据所在位置,如果不存在,迭代器为end位置的迭代器

    查找2:

    • map.lower_bound(key)
      返回map中第一个大于或者等于key的迭代器
    • map.upper_bound(key)
      返回map中第一个大于key的迭代器
    • map.equal_range(key)
      返回一个pair,pair中的第一个变量是lower_bound()返回的迭代器,
    • 第二个变量是upper_bound()返回的迭代器。
      -如果这两个迭代器相等的话,则说明map中不出现这个关键字
    //map的每一对映射都有两个typename,这决定了必须能通过一个 itr 来同时访问键和值。
    //map 可以使用 itr->first 来访问键, itr->second 来访问值。
    #include<iostream>
    #include<string>
    #include<map>using namespace std;
    
    int main() {
    map<char, int> mp;
    mp['m'] = 20;
    mp['r'] = 30;
    mp['a'] = 40;
    
    map<char,int>::iterator itr;
    for (itr = mp.begin(); itr != mp.end(); itr++) {
    cout << itr->first << " " << itr->second << endl;
    }
    
    //反向遍历
    map<char, int>::reverse_iterator itr1;
    for (itr1 = mp.rbegin(); itr1 != mp.rend(); itr1++) {
    cout << itr1->first << " " << itr1->second << endl;
    }
    
    cout << mp.count('a') << endl;//1
    cout << mp.count('b') << endl;//0
    
    map<char, int>::iterator itr2;
    itr2 = mp.find('m');
    if (itr2 != mp.end())
    cout << "Find,the value is : " << itr2->second << endl;
    else
    cout << "NOT FOUND!" << endl;
    
    map<int, string> mapStudent;
    mapStudent.insert(map<int, string>::value_type(1, "student_one"));
    mapStudent.insert(map<int, string>::value_type(3, "student_three"));
    mapStudent.insert(map<int, string>::value_type(5, "student_five"));
    map<int, string>::iterator iter;
    
    //返回第一个key大于或等于2的迭代器,所以返回的迭代器指向key=3的位置
    iter = mapStudent.lower_bound(2);
    cout << iter->second << endl;//student_three
    
    //返回第一个key大于2的迭代器,所以返回的迭代器指向key=3的位置
    iter = mapStudent.upper_bound(2);
    cout << iter->second << endl;//student_three
    
    //返回第一个key大于或等于3的迭代器,所以返回的迭代器指向key=3的位置
    iter = mapStudent.lower_bound(3);
    cout << iter->second << endl;//student_three
    
    //返回第一个key大于3的迭代器,所以返回的迭代器指向key=5的位置
    iter = mapStudent.upper_bound(3);
    cout << iter->second << endl;//student_five
    //
    pair<map<int, string>::iterator, map<int, string>::iterator > mappair;
    mappair = mapStudent.equal_range(2);
    if (mappair.first == mappair.second)
    cout << "NOT FOUND" << endl;
    else
    cout << "Find" << endl;
    mappair = mapStudent.equal_range(3);
    if (mappair.first == mappair.second)
    cout << "NOT FOUND" << endl;
    else
    cout << "Find" << endl;
    return 0;
    }
    }
    

    1.4.3、删除

    1、 erase(iterator it);  //删除 it 迭代器指向位置的元素
    
    2、erase(iterator first,iterator last);  //删除 first~last 范围的元素
    
    3、erase(const Key& key);  //通过key关键字删除
    
    4、clear()相当于erase(enumMap.begin(),enumMap.end())
    #include<map>#include<string>
    #include<iostream>
    using namespace std;
    int main()
    {
    map<int, string> mapStudent;
    mapStudent.insert(map<int, string>::value_type(1, "student_one"));
    mapStudent.insert(map<int, string>::value_type(2, "student_two"));
    mapStudent.insert(map<int, string>::value_type(3, "student_three"));
    
    //用迭代器删除1
    map<int, string>::iterator iter;
    iter = mapStudent.find(1);
    mapStudent.erase(iter);
    
    //用关键字删除1,返回0/1
    size_t n = mapStudent.erase(2);
    cout << n << endl;//1
    size_t  m = mapStudent.erase(1);
    cout << m << endl;//0,因为前面已经删除key = 1的位置了
    
    //只能下key = 3
    for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
    cout << iter->first << " " << iter->second << endl;
    
    mapStudent.clear();//清空
    //没有输出
    for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
    cout << iter->first << " " << iter->second << endl;
    
    mapStudent.insert(map<int, string>::value_type(1, "student_one"));
    mapStudent.insert(map<int, string>::value_type(2, "student_two"));
    mapStudent.insert(map<int, string>::value_type(3, "student_three"));
    
    mapStudent.erase(mapStudent.begin(), mapStudent.end());
    //没有输出
    for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
    cout << iter->first << " " << iter->second << endl;
    system("pause");
    }
    

    1.4.4、基本操作函数

    1、 begin() 返回指向map头部的迭代器
    2、end() 返回指向map末尾的迭代器
    3、size() 返回map中元素的个数
    4、empty() 如果map为空则返回true
    5、rbegin() 返回一个指向map尾部的逆向迭代器
    6、rend() 返回一个指向map头部的逆向迭代器

    参考

    1、https://www.geek-share.com/detail/2690215067.html
    2、https://blog.csdn.net/Li_haiyu/article/details/84974513
    3、https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: