STL库的map和set的使用

之前我们讲到过红黑树这个数据结构,STL库中还有两个非常重要的容器的底层是通过红黑树实现的,那就是map(图)和set(集合)。两者的表示上的区别在于,set存储的只是一个key,而map是以key和value的方式存储的(实际上是存储了一个pair的键值对,后面会讲到)。两者的接口基本类似,为了方便起见,这里就以set为准先来介绍。

set

set(集合)是按特定顺序存储独特元素的容器。在集合中,元素的值也标识它本身,也就是说,存储的就是key本身,每个值都必须是唯一的。集合中的元素不能修改,但是可以增加和删除。它的底层是以红黑树作为数据结构来实现的。下面我们来研究一下它的结构与接口。
一.成员函数
这里写图片描述
与所有容器一样的是,set也有它自己的构造函数,析构函数,以及赋值运算符重载。

二.迭代器
这里写图片描述
同样的,它也有属于自己的迭代器。begin返回容器中第一个元素的迭代器,end返回容器中跟随最后一个元素的理论元素的迭代器,rbegin返回容器中的最后一个元素的反向迭代器,rend返回容器中第一个元素之前的理论元素的迭代器。测试代码如下:

void Testset()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);
    }

    set<int>::iterator it=s.begin();
    while (it != s.end())
    {
        cout << *it << " ";
        it++;
    }

    /*set<int>::reverse_iterator rit = s.rbegin();
    while (rit != s.rend())
    {
        cout << *rit << " ";
        rit++;
    }*/
}

正向迭代器的测试结果如下
这里写图片描述
反向迭代器的测试结果如下
这里写图片描述

三.和容量相关的接口
这里写图片描述
empty判断set是否为空,size返回set的中元素的个数,max_size返回容器可以容纳的最大元素数,这三者的测试代码如下:

void Testset()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    int b , size , maxsize = 0;
    b = s.empty();
    size = s.size();
    maxsize = s.max_size();
    cout << b << " " << size << " " << maxsize << " ";

    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);
    }

    b = s.empty();
    size = s.size();
    maxsize = s.max_size();
    cout << b << " " << size << " " << maxsize << " ";
}

测试结果如下
这里写图片描述

四.关于集合内部结构的修改器
这里写图片描述
insert
这里写图片描述
有三种形式的插入,其中最常用的是第一、二种插入。第一种insert,它返回一个键值对pair,首先它将返回pair的第一个元素迭代器,指向新插入的元素的迭代器或是该元素已经存在指向该元素的迭代器,其次返回pair的第二个元素bool值,如果插入成功返回true,该元素失败则返回false,上面代码的insert用的都是这一种。第二种返回指向插入元素的迭代器,如果集合中已经有这个元素,则返回该元素位置的迭代器。第三种insert不常用,这里暂不作详解。
测试代码如下:

void Testset2()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    s.insert(s.begin(), a[0]);//第二种插入
    for (size_t i = 1; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);//第一种插入
    }

    set<int>::iterator it = s.begin();
    while (it != s.end())
    {
        cout << *it << " ";
        it++;
    }
}

这里写图片描述

erase
这里写图片描述
erase也有三种,第一种就是直接删除该位置上的元素,没有返回值;第二种删除返回删除元素的数量;第三种是删除first到last这一段区间上的元素。
下面是测试代码:

void Testset2()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);//第一种插入
    }

    s.erase(s.begin());//第一种删除,删除了0
    s.erase(1);//第二种删除,删除了1
    s.erase(s.begin(), s.end());//第三种删除,删除了set内的所有元素
    set<int>::iterator it = s.begin();
    while (it != s.end())
    {
        cout << *it << " ";
        it++;
    }
}

第一次删除后
这里写图片描述
第二次删除后
这里写图片描述
第三次删除后
这里写图片描述

swap
和另外一个set交换内容,这个很简单,就不测试了。

clear
清空集合

五.操作接口
这里写图片描述
find
查找元素,找到的话返回该元素的迭代器,找不到的话返回NULL

void Testset()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);
    }

    set<int>::iterator it=s.begin();
    it=s.find(5);
    //it=s.finf(10);
    //cout<<*it;
    while (it != s.end())
    {
        cout << *it << " ";
        it++;
    }

}

测试结果如下
这里写图片描述

count
计算集合中某一个特定元素的个数,因为集合中不会出现相同的元素,所以该接口只能返回1或0,经常用来判断集合中一个元素在不在,这里不作测试了。

lower_boundupper_bound
lower_bound返回第一个元素的迭代器,它不认为是在val之前的。也就是说,如果我们给5,那么我们返回的就是5的迭代器(5之前的指比5小的)。
upper_bound返回第一个元素的迭代器,它是在val之后的。。也就是说,如果我们给5,那么我们返回的就是6的迭代器(6是5之后的第一个元素)。
测试代码如下:

void Testset()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);
    }

    set<int>::iterator it=s.begin();
    it = s.lower_bound(5);
    //it = s.upper_bound(5);
    while (it != s.end())
    {
        cout << *it << " ";
        it++;
    }


}

两者的测试结果分别如下图
这里写图片描述

这里写图片描述

equal_range
返回一组范围的键值对
这里写图片描述
该范围最多只返回包括val在内的一个元素,如果val不存在则返回0,具体的我们直接看代码。

void Testset()
{
    int a[] = { 5, 6, 1, 9, 8, 3, 4, 2, 7, 0 };
    set<int> s;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        s.insert(a[i]);
    }

    pair<set<int>::iterator, set<int>::iterator> ret;
    ret = s.equal_range(1);
    //ret= s.equal_range(10);
    cout << "the lower bound points to: " << *ret.first << endl;
    cout << "the upper bound points to: " << *ret.second << endl;


}

这里写图片描述

map

map(图,也称映射)是由键值和映射值组合而成的存储元素的关联容器,遵循特定的顺序。在map中,键值通常用于排序和唯一标识元素(pair类型的first),而映射值存储与此键相关的内容(pair类型的second)。键和映射值的类型可能不同,并在成员类型value_type中组合在一起,这是一对组合的类型

typedef pair<const Key, const Value> value_type;

在这里我们为什么用pair,不用K,V呢?pair类型到底是一个怎样的类型呢?
pair的具体形式可以用下面的代码来描述

template <class K,class V>
struct pair
{
    K first;
    V second;

    pair(const K* key, const V* value)
        :first(key)
        , second(value)
    {}
};

它的first是K类型,second是V类型。在迭代器取数据时返回值要求同时返回两个值,但是C++中一个函数只能返回一个值,所以存储类型要用pair。

map内的接口set差不多,我们先总体来看一下再看一下它们的区别
这里写图片描述
这里写图片描述

在这里基本上的接口都一样,最主要的区别就是map多了一个接口是operator[],这个接口的用处非常广泛。它的参数是key,返回值是value的引用。实际上它实现的就是下面的代码

(*((this->insert(make_pair(k,mapped_type()))).first)).second

我们将上述代码分开来看一下
①.this->insert(make_pair(k,mapped_type()))
这一部分创建了一个pair对象,它的first是key,second是value的类型的缺省值,然后是在当前的map对象中插入这个pair对象
②.*(this->insert(make_pair(k,mapped_type()))).first)
第二部分取出了pair的first,并对first进行解引用取到它的值,我们就取到了关键字是key的这个对象
③.(*((this->insert(make_pair(k,mapped_type()))).first)).second
第三部分,取出关键字为key这个对象的second,也就是value,之后的返回值也就是value的引用。

在插入的时候,如果map中没有要插入的key值,则插入一个key,value为V的类型的缺省值的pair对象,并返回对value的引用。如果map中已经有key这个关键字,则会直接返回value的引用。具体的测试代码如下:

void Testmap1()
{
    map<string, string> m;
    m["student"] = "学生";
    m["west"];
    m["left"] = "左边";
    m["right"] = "右边";
    m["student"]= "老师";
}

如上,关键字west的value给的是缺省的,对于字符串而言,其缺省值就是NULL。另外在第二次插入student关键字时,又对它的value重新进行了赋值,我们可以通过监视窗口来发现两者的变化。
下面是第二个student插入前的窗口
这里写图片描述
下面是第二个student插入后的窗口
这里写图片描述

map和set都是用的非常广泛的容器,对于这两个容器的使用必须熟练掌握。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值