当前位置:   article > 正文

c++ 常用STL 之set_std::set内存结构

std::set内存结构

在文章c++ 常用STL 之vector_kangshuangzhu的博客-CSDN博客

中介绍了序列容器,并且系统介绍了vector的用法。不难发现,无论是哪种序列式容器,其存储的都是 C++ 基本数据类型(诸如 int、double、float、string 等)或使用结构体自定义类型的元素。例如,如下是一个存储 int 类型元素的 vector 容器:

std::vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19};
关联式容器则大不一样,此类容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为“键”,其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。

常用的关联式容器包括set map

下面会着重介绍一下set

set  定义在 <set> 头文件中,使用该容器存储的数据,各个元素键和值完全相同,且各个元素的值不能重复(保证了各元素键的唯一性)。该容器会自动根据各个元素的键(其实也就是元素值)的大小进行升序排序(调用 std::less<T>)。

set的存储方式是平衡二叉树,所以可以快速实现有序的插入元素

 这里仍然从初始化,增,删,改,查5个方面入手,

初始化:

set的初始化和vector比较像,可以空初始化;赋值初始化;拷贝其他set,但是拷贝初始化的时候拷贝的一定要是另一个set,如果是其他的容器则会报错。

  1. std::vector<int> my_vec = {1,2,3,3,4,3,6};
  2. std::set<int> s1; // 初始化一个空set
  3. std::set<int> s2 = {4,5,3,6,2,7}; // 赋值初始化一个set
  4. std::set<int> s3(s2); // 拷贝s2的元素
  5. std::set<int> s3(my_vec); // 报错
  6. std::set<int> s4(s2.begin(),++s2.begin()); // 取某一段内存的数据初始化
  7. std::set<int> s4(s2.begin(),s2.begin()+3); //报错,set迭代器不支持这种运算
  8. std::set<int> s5(my_vec.begin(), my_vec.end()); // 取某一段内存的数据初始化
  9. std::set<int> s6 = {my_vec.begin(), my_vec.end()}; // 取某一段内存的数据初始化

应用最灵活的还是用迭代器初始化,同样的,用迭代器初始化,不要求迭代器是set的迭代器,任何迭代器都可以。

应该格外注意的是set的迭代器不支持s.begin()+3 这种运算,但是却支持s.begin()++ 和 ++ s.begin() 。后面如果找到原因会在这里补上。

查:

因为元素是有序的插入到set中的,所以,set是无序的,不能用下表或者at函数来取某一个值。

find 查询set是否包含某一个元素,如果存在则返回迭代器,如果没有则返回end

  1. std::set<int> s2 = {4,5,3,6,2,7}; // 赋值初始化一个set
  2. auto k = s2.find(3);
  3. std::cout << "(k == s2.end()) " << (k == s2.end()) << std::endl;
  4. auto s = s2.find(30);
  5. std::cout << "(s == s2.end()) " << (s == s2.end()) << std::endl;
  6. 输出
  7. (k == s2.end()) 0
  8. (s == s2.end()) 1

size()  获取set的大小

empty()set是否为空

  1. std::set<int> s2 = {4,5,3,6,2,7}; // 赋值初始化一个set
  2. std::cout << "set size " << s2.size() << std::endl;
  3. 输出
  4. set size 6

遍历set

set的遍历方式有两种。因为set时无需集合,所以不能通过下标的方式遍历。

  1. std::set<int> s2 = {4,5,3,6,2,7}; // 赋值初始化一个set
  2. for (auto i: s2){
  3. std::cout << i << " ";
  4. }
  5. for(auto i = s2.begin(); i != s2.end(); i++){
  6. std::cout << *i << " ";
  7. }
  8. for(auto i = s2.begin(); i < s2.end(); i++){ //报错
  9. std::cout << *i << " ";
  10. }

set的第二种遍历有一个地方需要注意,终止循环的条件不能写成 

i < s2.end()

而要写成

i != s2.end()

set提供一个insert函数用于新增元素,因为set会对元素自动排序,新增元素的时候无所谓插入位置。虽然insert 可以指定插入位置,但插入后会自动排序,所以指定的插入位置没有用

  1. std::set<int> s2 = {4,5,3,6,2,7};
  2. s2.insert(10);
  3. for (auto i: s2){
  4. std::cout << i << " ";
  5. }
  6. s2.insert(s2.begin(), 20);
  7. for(auto i = s2.begin(); i != s2.end(); i++){
  8. std::cout << *i << " ";
  9. }
  10. 输出
  11. 2 3 4 5 6 7 10
  12. 2 3 4 5 6 7 10 20

这种指定插入位置的用法可能会逐渐废弃,所以不推荐使用

当然insert还是支持插入一个迭代器的,在插入迭代器的时候,不能指定插入位置。例如

  1. std::set<int> s2 = {4,5,3,6,2,7};
  2. std::set<int> s3({-1,-2,-3,-4});
  3. s2.insert({10,20,30});
  4. for (auto i: s2){
  5. std::cout << i << " ";
  6. }
  7. s2.insert(s3.begin(), s3.end());
  8. for(auto i = s2.begin(); i != s2.end(); i++){
  9. std::cout << *i << " ";
  10. }
  11. 输出
  12. 2 3 4 5 6 7 10 20 30
  13. -4 -3 -2 -1 2 3 4 5 6 7 10 20 30

删:

clear(),删除set 所有值

set的删除函数时erase,因为set的元素有唯一性,所以对于基本数据结构可以直接输入一个值进行删除

  1. std::set<int> s2 = {4,5,3,6,2,7};
  2. s2.erase(6);
  3. for (auto i: s2){
  4. std::cout << i << " ";
  5. }
  6. 输出
  7. 2 3 4 5 7

当然也可以通过迭代器删除,但是因为set迭代器不支持 begin()+2 以及 < , > , <=,  >= 这些操作。所以想删除某一段数据会比较繁琐。

  1. std::set<int> s2 = {4,5,3,6,2,7};
  2. auto it = s2.find(5);
  3. s2.erase(it,s2.end());
  4. for (auto i: s2){
  5. std::cout << i << " ";
  6. }
  7. 输出
  8. 2 3 4

swap 与 vector相同,可以看文章c++ 常用STL 之vector_kangshuangzhu的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/382960
推荐阅读
相关标签
  

闽ICP备14008679号