C++ STL set容器

等待樱花盛开

一、前言

和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。如下:

{<'a', 'a'>, <'b', 'b'>, <'c', 'c'>}

不支持存储键和值不相等的键值对。因此在使用set容器时,只需要为其提供各键值对中的value值即可。set容器和map容器一样,会自行根据键的大小对存储的键值对进行排序,在set容器中,实际上也是根据value值进行排序。
set 容器存储的各个元素的值必须各不相同。需要注意的是:在语法上set容器并没有强制对存储元素的类型做const修饰,因此set容器中存储的元素的值是可以修改的。但是,C++ 标准为了防止用户修改容器中元素的值,对所有可能会实现此操作的行为做了限制,使得在正常情况下,用户是无法做到修改 set 容器中元素的值的
注意:直接修改set容器中的已存储的元素的值,可能会破坏set容器中元素的有序性。最正确的修改 set 容器中元素值的做法是:先删除该元素,然后再添加一个修改后的元素。使用set容器,需要

#include <set>

1、set容器类模板的定义

template < class T,     // 键 key 和值 value 的类型
           class Compare = less<T>,  // 指定 set 容器内部的排序规则
           class Alloc = allocator<T>  // 指定分配器对象的类型
           > class set;

因为键和值的类型是一样的,因此这里只有3个参数,大多数情况下,主需要用到其中的前2个参数。

二、创建set容器的方法

常见的创建 set 容器的方法,大致有以下 5 种。

①std::set<std::string> myset;
②std::set<std::string> myset{"1","2","3"};
③std::set<std::string> copyset(myset);
④std::set<std::string> myset{"1","2","3"};
std::set<std::string> copyset(++myset.begin(), myset.end());
⑤std::set<std::string,std::greater<string> > myset{"1","2","3"};

①默认构造函数,创建空的set容器,会对存储的 string 类型元素做升序排序
②在创建 set 容器的同时,对其进行初始化
③通过拷贝(复制)构造函数,实现在创建新 set 容器的同时,将已有 set 容器中存储的所有元素全部复制到新 set 容器中,等价于std::set<std::string> copyset = myset;C++ 11 标准还为 set 类模板新增了移动构造函数,其功能是实现创建新 set 容器的同时,利用临时的 set 容器为其初始化,如下:

set<string> retSet() 
{
    std::set<std::string> myset{ "1","2","3" };
    return myset;   //返回值是一个临时 set 容器,因此在初始化 copyset 容器时,其内部调用的是 set 类模板中的移动构造函数,而非拷贝构造函数。
}
std::set<std::string> copyset(retSet());
//等价于 std::set<std::string> copyset = retSet();

④set 类模板取已有 set 容器中的部分元素,来初始化新 set 容器
⑤、修改 set 容器中的排序规则方式

三、方法

这里不列举迭代器、插入和删除相关的方法。

方法功能
empty()若容器为空,则返回 true;否则 false
size()返回当前 set 容器中存有元素的个数
max_size()返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同
count(val)在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1

1、实例

1)、创建set容器

方式1

std::set<std::string> myset;
std::cout << "myset size: " << myset.size() << std::endl;

方式2

std::set<std::string> myset1{ 
                            "who are you",
                            "i am lin",
                            "i am wu",
                            "i am ouyang"};
std::cout << "myset1 size: " << myset1.size() << std::endl;
std::set<std::string>::iterator setIter = myset1.begin();
for (; setIter != myset1.end(); setIter++)
{
    std::cout << "myset1 :" << *setIter << std::endl;
}

方式3

std::set<std::string> myset2(myset1);
std::cout << "myset2 size: " << myset2.size() << std::endl;
setIter = myset2.begin();
for (; setIter != myset2.end(); setIter++)
{
    std::cout << "myset2 :" << *setIter << std::endl;
}

移动构造函数方式

set<string> RemoveStructureSet()
{
    std::set<std::string> myset{ 
                            "who are you",
                            "i am wen",
                            "i am li" };
    return myset;
}

std::set<std::string> myset3(RemoveStructureSet()); 
//等价于 std::set<std::string> myset3 = RemoveStructureSet();

setIter = myset3.begin();
for (; setIter != myset3.end(); setIter++)
{
    std::cout << "myset3 :" << *setIter << std::endl;
}

方式4

std::set<std::string> myset4(++myset.begin(), --myset.end()); 
setIter = myset4.begin();
for (; setIter != myset4.end(); setIter++)
{
    std::cout << "myset4 :" << *setIter << std::endl;
}

方式5

std::set<std::string, std::greater<string> > myset5{
                            "who are you",
                            "i am lin",
                            "i am wu",
                            "i am ouyang" };

setIter = myset5.begin();
for (; setIter != myset5.end(); setIter++)
{
    std::cout << "myset5 :" << *setIter << std::endl;
}

结果如下:
image.png

四、迭代器

set 容器类模板中未提供 at() 成员函数,也未对 [] 运算符进行重载。C++ STL 标准库为 set 容器配置的迭代器类型为双向迭代器,则set容器的迭代器支持++p、p++、--p、p--、*p 操作,并且 2 个双向迭代器之间做比较,只能使用 == 或者 != 运算符。

方法功能
begin()返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
end()返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
rbegin()返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器
rend()返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。通常和 rbegin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值
find(val)在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
lower_bound(val)返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
upper_bound(val)返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
equal_range(val)该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)

以上成员函数返回的迭代器,指向的只是 set 容器中存储的元素,而不再是键值对。以上成员方法返回的迭代器,无论是 const 类型还是非 const 类型,都不能用于修改 set 容器中的值。
begin和end方法在这里不再说明。equal_range(val) 函数的返回值是一个 pair 类型数据,其包含 2 个迭代器,表示 set 容器中和指定参数 val 相等的元素所在的区域,但由于 set 容器中存储的元素各不相等,因此该函数返回的这 2 个迭代器所表示的范围中,最多只会包含 1 个元素。

1、实例

1)、find和count

std::set<std::string> myset1{ 
                            "who are you",
                            "i am lin",
                            "i am wu",
                            "i am ouyang"};
std::cout << "myset1 size: " << myset1.size() << std::endl;
std::set<std::string>::iterator setIter = myset1.begin();
for (; setIter != myset1.end(); setIter++)
{
    std::cout << "myset1 :" << *setIter << std::endl;
}

//find
set<std::string>::iterator findIter = myset1.find("i am wu");
cout << "findIter: " << *findIter << endl;

//count
int nCount = myset1.count("i am wu");
cout << "nCount: " << nCount << endl;

2)、lower_bound和upper_bound

set<std::string>::iterator lowerIter = myset1.lower_bound("i am wu");
cout << "lowerIter: " << *lowerIter << endl;

set<std::string>::iterator upperIter = myset1.upper_bound("i am wu");
cout << "upperIter: " << *upperIter << endl;

3)、equal_range

pair<set<std::string>::iterator, set<std::string>::iterator>  mypair = myset1.equal_range("i am wu");
set<std::string>::iterator pairIter1 = mypair.first;
cout << "pairIter1: " << *pairIter1 << endl;

set<std::string>::iterator pairIter2 = mypair.second;
cout << "pairIter2: " << *pairIter2 << endl;

结果如下:
image.png

五、插入数据

1、insert

1)直接将键的值插入set容器

格式说明
pair<iterator,bool> insert (const value_type& val);普通引用方式传参
pair<iterator,bool> insert (value_type&& val);右值引用方式传参

以上 2 种语法格式的 insert() 方法,返回的都是 pair 类型的值,其包含 2 个数据,一个迭代器和一个 bool 值:
当向 set 容器添加元素成功时,该迭代器指向 set 容器新添加的元素,bool 类型的值为 true;
如果添加失败,即证明原 set 容器中已存有相同的元素,此时返回的迭代器就指向容器中相同的此元素,同时 bool 类型的值为 false。

std::set<std::string> myset1;

//格式1
pair<set<std::string>::iterator, bool> ret;
string str = "i am lin";
ret = myset1.insert(str);
cout << "one iter->" << *(ret.first) << " , " << "bool = " << ret.second << endl;

//格式2
ret = myset1.insert("i am huang");
cout << "two iter->" << *(ret.first) << " , " << "bool = " << ret.second << endl;


//插入失败
ret = myset1.insert("i am huang");
cout << "failed iter->" << *(ret.first) << " , " << "bool = " << ret.second << endl;


std::cout << "myset1 size: " << myset1.size() << std::endl;
std::set<std::string>::iterator setIter = myset1.begin();
for (; setIter != myset1.end(); setIter++)
{
    std::cout << "myset1 :" << *setIter << std::endl;
}

结果如下:
image.png

2)指定将新元素插入到 set 容器中的具体位置

格式说明
iterator insert (const_iterator position, const value_type& val);以普通引用的方式传递 val 值
iterator insert (const_iterator position, value_type&& val);以右值引用的方式传递 val 值

以上 2 种语法格式中,insert() 函数的返回值为迭代器:
当向 set 容器添加元素成功时,该迭代器指向容器中新添加的元素;
当添加失败时,证明原 set 容器中已有相同的元素,该迭代器就指向 set 容器中相同的这个元素。

std::set<std::string> myset1;
set<std::string>::iterator iter;

//格式1
string str = "i am lin";
iter = myset1.insert(myset1.begin(), str);
cout << "one insert iter->" << *iter << endl;

//格式2
iter = myset1.insert(myset1.end(), "i am huang");
cout << "two insert iter->" << *iter << endl;


std::cout << "myset1 size: " << myset1.size() << std::endl;
std::set<std::string>::iterator setIter = myset1.begin();
for (; setIter != myset1.end(); setIter++)
{
    std::cout << "myset1 :" << *setIter << std::endl;
}

结果如下:
image.png
使用 insert() 方法将目标元素插入到 set 容器指定位置后,如果该元素破坏了容器内部的有序状态,set 容器还会自行对新元素的位置做进一步调整,即会判断有序性是否成立,不成立会调整键值对顺序。

3)向当前 set 容器中插入其它 set 容器指定区域内的所有元素

前提:2 个 set 容器存储的元素类型相同即可
template <class InputIterator> void insert (InputIterator first, InputIterator last);插入其它 set 容器指定区域内的所有元素
std::set<std::string> myset1{
                    "i am wang",
                    "i am wu",
                    "i am a",
                    "i am kai"};

std::set<std::string> myset2;
myset2.insert(++myset1.begin(), --myset1.end());

std::cout << "myset2 size: " << myset2.size() << std::endl;
std::set<std::string>::iterator setIter = myset2.begin();
for (; setIter != myset2.end(); setIter++)
{
    std::cout << "myset2 :" << *setIter << std::endl;
}

结果如下:
image.png

4)一次性插入多个元素

void insert ( {E1, E2,...,En} );
std::set<std::string> myset1{
                    "i am wang",
                    "i am wu",
                    "i am a",
                    "i am kai"};

myset1.insert({ "i am ouyang","i am fang", "i am lu", "i am xu" });

std::cout << "myset1 size: " << myset1.size() << std::endl;
std::set<std::string>::iterator setIter = myset1.begin();
for (; setIter != myset1.end(); setIter++)
{
    std::cout << "myset1 :" << *setIter << std::endl;
}

结果如下:
image.png
C++ 11 标准的 set 类模板中,还提供有另外 2 个成员方法,分别为 implace() 和 implace_hint() 方法,借助它们不但能实现向 set 容器添加新元素的功能,其实现效率也比 insert() 成员方法更高。

2、emplace

template <class... Args> pair<iterator,bool> emplace (Args&&... args);

只需要传入构建新元素所需的数据即可,该方法可以自行利用这些数据构建出要添加的元素。另外,该方法的返回值类型为 pair 类型,其包含 2 个元素,一个迭代器和一个 bool 值:
当该方法将目标元素成功添加到 set 容器中时,其返回的迭代器指向新插入的元素,同时 bool 值为 true;
当添加失败时,则表明原 set 容器中已存在相同值的元素,此时返回的迭代器指向容器中具有相同键的这个元素,同时 bool 值为 false。

std::set<std::string> myset;
pair<set<std::string, std::string>::iterator, bool> ret = myset.emplace("i am lin");

cout << "ret.iter = < value: " << *(ret.first) << ", result: " << ret.second << ">" << endl;

结果如下:
image.png

3、emplace_hint

template <class... Args> iterator emplace_hint (const_iterator position, Args&&... args);

和 emplace() 方法相比:
该方法需要额外传入一个迭代器,用来指明新元素添加到 set 容器的具体位置(新元素会添加到该迭代器指向元素的前面);
返回值是一个迭代器,而不再是 pair 对象。当成功添加元素时,返回的迭代器指向新添加的元素;反之,如果添加失败,则迭代器就指向 set 容器和要添加元素的值相同的元素。

std::set<std::string> myset{"i am wu","i am kai","i am yang"};
set<std::string>::iterator iter = myset.emplace_hint(++myset.begin(), "i am lin");

cout << "iter = < value: " << *iter << " >" << endl;

std::cout << "myset size: " << myset.size() << std::endl;
std::set<std::string>::iterator setIter = myset.begin();
for (; setIter != myset.end(); setIter++)
{
    std::cout << "myset1 :" << *setIter << std::endl;
}

结果如下:
image.png

六、删除元素

删除 set 容器存储的元素,可以选择用 erase() 或者 clear() 成员方法。

1、erase方法

语法说明
size_type erase (const value_type& val);删除 set 容器中值为 val 的元素
iterator erase (const_iterator position);删除 position 迭代器指向的元素
iterator erase (const_iterator first, const_iterator last);删除 [first,last) 区间内的所有元素

①方法返回值是一个整数,表示成功删除的元素个数
②③方法返回值都是迭代器,其指向的是 set 容器中删除元素之后的第一个元素
注意:如果要删除的元素就是set容器最后一个元素,则erase()方法返回的迭代器就指向新 set 容器中最后一个元素之后的位置(等价于end()方法返回的迭代器)

2、clear

删除set容器中存储的所有元素,不需要参数,也没有返回值

因删除元素函数比较简单,这里就不写示例了

总结

1、不再以键值对的方式存储数据,因为 set 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value)
2、set 容器在存储数据时,会根据各元素值的大小对存储的元素进行排序(默认做升序排序);
3、存储到 set 容器中的元素,虽然其类型没有明确用 const 修饰,但正常情况下它们的值是无法被修改的
4、set 容器存储的元素必须互不相等

阅读 924

小菜鸟一枚,望各位大佬不吝指教,用于记录学习

1 声望
0 粉丝
0 条评论
你知道吗?

小菜鸟一枚,望各位大佬不吝指教,用于记录学习

1 声望
0 粉丝
文章目录
宣传栏