STL容器 map 与 set 的用法

本文介绍了STL中的map和set容器,包括它们的用途、操作方法以及底层实现。map是一种键值对结构,不允许重复键值,而set是无序不重复元素集。两者都基于红黑树实现,提供了插入、删除、查找等操作。此外,还讨论了multiset和multimap,它们允许元素重复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

认识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中,它的底层是用红黑树实现的,它的insert机制是insert_equal。它的count与multiset的count的用法及作用完全一样。下面看一下他们的不同:
1、insert
   iterator insert(const value_type& val)
   如果插入一个map的元素的话,multimap会返回一个这个插入位置的迭代器。
2、count
   count与multiset的count完全相同,multimap中的count统计的是键值出现的次数。

3、multimap中没有operator[],这个很好理解,当你调用operator[]的时候会对应多个结果,返回哪个都不合适,所以也就没有operator[]。

其他操作与set基本相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值