STL中的set详解

2023-05-16

1.关于set

C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生。

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

 关于set有下面几个问题:

(1)为何map和set的插入删除效率比用其他序列容器高?

大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:

 

   A
   / \
  B C
 / \ / \
  D E F G

因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。

(2)为何每次insert之后,以前保存的iterator不会失效?

iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。

(3)当数据元素增多时,set的插入和搜索速度变化如何?

如果你知道log2的关系你应该就彻底了解这个答案。在set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。你明白这个道理后,就可以安心往里面放入元素了。

set使用方法:

begin()        ,返回set容器的第一个迭代器

end()      ,返回set容器的最后一个迭代器

clear()          ,删除set容器中的所有的元素

empty()    ,判断set容器是否为空

max_size()   ,返回set容器可能包含的元素最大个数

size()      ,返回当前set容器中的元素个数

rbegin     ,返回的值和end()相同

rend()     ,返回的值和rbegin()相同

find()                ,返回这个元素的迭代器,如果没有就返回end()

简单操作实例:

// set::find
#include <iostream>
#include <set>

int main ()
{
  std::set<int> myset;
  std::set<int>::iterator it;

  // set some initial values:
  for (int i=1; i<=5; i++) myset.insert(i*10);    // set: 10 20 30 40 50

  it=myset.find(20);
  myset.erase (it);
  myset.erase (myset.find(40));

  std::cout << "myset contains:";
  for (it=myset.begin(); it!=myset.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}
#include <iostream>  
#include <set>  
using namespace std;  
int main()  
{  
    set<int> s;  
    s.insert(1);  
    s.insert(2);  
    s.insert(3);  
    s.insert(1);  
    cout<<"set 的 size 值为 :"<<s.size()<<endl;  
    cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;  
    cout<<"set 中的第一个元素是 :"<<*s.begin()<<endl;  //1
    cout<<"set 中的最后一个元素是:"<<*s.end()<<endl;  //3
    s.clear();  
    if(s.empty())  
    {  
        cout<<"set 为空 !!!"<<endl;  
    }  
    cout<<"set 的 size 值为 :"<<s.size()<<endl;  //3
    cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;  //3
    return 0;  
}  

运行结果:
小结:插入3之后虽然插入了一个1,但是我们发现set中最后一个值仍然是3哈,这就是set 。还要注意begin() 和 end()函数是不检查set是否为空的,使用前最好使用empty()检验一下set是否为空。

count() 用来查找set中某个某个键值出现的次数。这个函数在set并不是很实用,因为一个键值在set只可能出现0或1次,这样就变成了判断某一键值是否在set出现过了。

示例代码:

#include <iostream>  
#include <set>    
using namespace std;  
  
int main()  
{  
    set<int> s;  
    s.insert(1);  
    s.insert(2);  
    s.insert(3);  
    s.insert(1);  
    cout<<"set 中 1 出现的次数是 :"<<s.count(1)<<endl;  //1
    cout<<"set 中 4 出现的次数是 :"<<s.count(4)<<endl;  //0
    return 0;  
}  

equal_range() ,返回一对定位器,分别表示第一个大于或等于给定关键值的元素和 第一个大于给定关键值的元素,这个返回值是一个pair类型,如果这一对定位器中哪个返回失败,就会等于end()的值。具体这个有什么用途我还没遇到过~~~

#include <iostream>  
#include <set>  
using namespace std;  
  
int main()  
{  
    set<int> s;  
    set<int>::iterator iter;  
    for(int i = 1 ; i <= 5; ++i)  
    {  
        s.insert(i);  
    }  
    for(iter = s.begin() ; iter != s.end() ; ++iter)  
    {  
        cout<<*iter<<" ";  
    }  
    cout<<endl;  
    pair<set<int>::const_iterator,set<int>::const_iterator> pr;  
    pr = s.equal_range(3);  
    cout<<"第一个大于等于 3 的数是 :"<<*pr.first<<endl;  
    cout<<"第一个大于 3的数是 : "<<*pr.second<<endl;  
    return 0;  
}  
  • erase(iterator)  ,删除定位器iterator指向的值
  • erase(first,second),删除定位器first和second之间的值
  • erase(key_value),删除键值key_value的值

看看程序吧:

#include <iostream>  
#include <set>    
using namespace std;  
  
int main()  
{  
    set<int> s;  
    set<int>::const_iterator iter;  
    set<int>::iterator first;  
    set<int>::iterator second;  
    for(int i = 1 ; i <= 10 ; ++i)  
    {  
        s.insert(i);  
    }  
    //第一种删除  
    s.erase(s.begin());  
    //第二种删除  
    first = s.begin();  
    second = s.begin();  
    second++;  
    second++;  
    s.erase(first,second);  
    //第三种删除  
    s.erase(8);  
    cout<<"删除后 set 中元素是 :";  
    for(iter = s.begin() ; iter != s.end() ; ++iter)  
    {  
        cout<<*iter<<" ";  
    }  
    cout<<endl;  
    return 0;  
}  

运行结果:

小结:set中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。

find()  ,返回给定值值得定位器,如果没找到则返回end()。

示例代码:

#include <iostream>  
#include <set>   
using namespace std;  
  
int main()  
{  
    int a[] = {1,2,3};  
    set<int> s(a,a+3);  
    set<int>::iterator iter;  
    if((iter = s.find(2)) != s.end())  
    {  
        cout<<*iter<<endl;  
    }  
    return 0;  
}  

insert(key_value); 将key_value插入到set中 ,返回值是pair<set<int>::iterator,bool>,bool标志着插入是否成功,而iterator代表插入的位置,若key_value已经在set中,则iterator表示的key_value在set中的位置。

inset(first,second);将定位器first到second之间的元素插入到set中,返回值是void.

示例代码:

#include <iostream>  
#include <set>   
using namespace std;  
  
int main()  
{  
    int a[] = {1,2,3};  
    set<int> s;  
    set<int>::iterator iter;  
    s.insert(a,a+3);  
    for(iter = s.begin() ; iter != s.end() ; ++iter)  
    {  
        cout<<*iter<<" ";  
    }  
    cout<<endl;  
    pair<set<int>::iterator,bool> pr;  
    pr = s.insert(5);  
    if(pr.second)  
    {  
        cout<<*pr.first<<endl;  
    }  
    return 0;  
}  

lower_bound(key_value) ,返回第一个大于等于key_value的定位器

upper_bound(key_value),返回最后一个大于等于key_value的定位器

示例代码:

#include <iostream>  
#include <set>    
using namespace std;  
  
int main()  
{  
    set<int> s;  
    s.insert(1);  
    s.insert(3);  
    s.insert(4);  
    cout<<*s.lower_bound(2)<<endl;  
    cout<<*s.lower_bound(3)<<endl;  
    cout<<*s.upper_bound(3)<<endl;  
    return 0;  
}  

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STL中的set详解 的相关文章

  • 使用 std::set 时重载运算符<

    这是我第一次使用 std set 容器 并且我对操作符 std less 遇到了问题 我声明该集合 std set
  • STL(标准模板库)中使用的设计模式

    我正在学习STL和设计模式 我想知道是否有任何文档或链接可以解释如何在 STL 中实现设计模式 我做了谷歌但无法获得太多数据 我希望你的意思是 哪些设计模式可以在STL中识别 STL 堆栈是一个容器适配器 适配器是一种设计模式 迭代器也是一
  • 了解 std::swap()。 tr1::_Remove_reference 的目的是什么?

    在 VS10 的 STL 实现中 有以下 std swap 变体的代码 TEMPLATE FUNCTION Move template
  • dict_values 视图什么时候可以像设置一样(以及为什么)?

    文档说值视图不被视为类似集合 https docs python org 3 library stdtypes html dictionary view objects 但有时它们是 gt gt gt d 1 1 gt gt gt d va
  • 使用 std::min_element() 时保存函数计算

    假设给你一个 2D 点向量 并期望找到最少的点欧几里得范数 http en wikipedia org wiki Norm 28mathematics 29 Euclidean norm 点提供为std vector
  • std::enable_if 和 std::enable_if_t 有什么区别?

    C 14 引入std enable if t 它和有什么区别std enable if 使用上有什么优点或者区别吗std enable if t std enable if t 是 std enable if 的内部 type 的类型别名
  • C++类名冲突

    我现在正在做一个项目 需要整合两个子项目 项目A是用C 编写的 项目B是用C编写的 一个问题是 在项目B中 有一个名为vector它是由其作者创建的 在项目 A 中 std vector in STL用来 因为项目B以后可能会更新 所以我不
  • std::类似向量的类经过优化以容纳少量项目[重复]

    这个问题在这里已经有答案了 在程序的一个时间关键部分中 有一个类成员如下所示 std vector m vLinks 在分析过程中 我注意到该向量大约 99 98 的执行仅包含 0 或 1 个项目 然而 在极少数情况下 它可能会容纳更多 根
  • std::map 和二叉搜索树

    我读过 std map 是使用二叉搜索树数据结构实现的 BST 是一种顺序数据结构 类似于数组中的元素 它将元素存储在 BST 节点中并按其顺序维护元素 例如如果元素小于节点 则将其存储在节点的左侧 如果元素大于节点 则将其存储在节点的右侧
  • MySQL - 从数字列表中选择在表的 id 字段中没有对应项的数字

    我有一个数字列表 例如 2 4 5 6 7 我有一个表 foos 带有 foos ID 包括 1 2 3 4 8 9 我想获取我的号码列表 并在我的表的 ID 字段中找到那些没有对应项的号码 实现此目的的一种方法是创建一个表格栏 在 ID
  • std::vector::data() 的状态是什么?

    我刚刚意识到我一直在使用std vector data 出于与 std string 的相似性 但一位同事指出它不是标准的 显然 Gcc 实现了它 但是查看它的包含文件 我发现了这样的注释 GLIBCXX RESOLVE LIB DEFEC
  • Swift 结构类型集

    说我有一个struct 可以是任何东西 struct Cube var x Int var y Int var z Int var width Int 然后我该如何创建一个Set这些点中 是否存在两个具有相同属性的对象 let points
  • 下界比较函数

    我有以下结构 enum quality good 0 bad uncertain struct Value int time int value quality qual class MyClass public MyClass Inser
  • 用于查找列表/集合中唯一元素的代码

    根据上面阴影部分的面积应该代表 A XOR B XOR C XOR A AND B AND C 如何将其翻译成Python代码 代码必须与上述表达式中提供的集合操作密切相关 至少这是首选 该代码必须足够通用 能够处理 3 个以上的列表 UP
  • STL之类的容器typedef快捷方式?

    STL 容器的常见模式是这样的 map
  • 如何将STL容器数据转储到gdb中?

    我无法在 gdb 中转储 STL 无序映射容器值 变量类型是 std unordered map var 我的 gdb 版本 7 7 1 GDB配置 configure host x86 64 linux gnu target x86 64
  • 使用 pybind11 修改 std::array 的默认值

    我的目标是修改在中声明的数组C struct并赋予默认值 我读过了this https pybind11 readthedocs io en stable advanced cast stl html making opaque types
  • allocator.construct 循环是否等于 std::uninitialized_copy?

    在此背景下T是某种类型并且allocator是该类型的分配器对象 默认情况下是std allocator
  • 我应该将哪个 STL 容器用于 FIFO?

    哪种 STL 容器最适合我的需求 我基本上有一个 10 个元素宽的容器 我在其中不断地push back新元素的同时pop front计算最旧的元素 大约一百万次 我目前正在使用std deque对于这项任务 但想知道是否std list会
  • 为什么我不能执行 std::map.begin() + 1?

    我有一个std map 我想从第二个条目开始迭代 我可以解决这个问题 但我对为什么 明显 语法无法编译感到困惑 该错误消息没有帮助 因为它指的是std string 我在这里没有使用它 这是一些代码 Suppose I have some

随机推荐

  • 清华大学研究成果:如何用博弈论解决自动驾驶路口的会车决策问题?

    雷锋网新智驾按 xff1a 4月24日 xff0c 雷锋网新智驾联合MMC在2017年上海车展举办 构建智能驾驶的关键 主题沙龙 xff0c 本文来自清华大学自动化系统工程研究所教授姚丹亚的分享 本文讲述了V2X技术在自动驾驶中的一个重要应
  • Sonarqube集成到Jenkins实现代码自动检测

    如何使用Sonar把不同的代码检查工具结果直接显示在WEB页面上 xff1f xff1f 详细实操如下 xff1a 需要配置 Sonarqube服务端 xff1a 192 168 10 12Sonarqube客户端postgres 数据库
  • 车路协调系统图

  • c++ 两个vector之间相互赋值,或在一个后面追加另一个

    方法1 xff1a vector lt int gt v1 v2 声明 方法2 xff1a vector lt int gt v1 v1 swap v2 将两个容器内的元素交换 需要构建临时对象 xff0c 一个拷贝构造 xff0c 两次赋
  • 6月8 力扣 回文数和验证回文串

    9 回文数 难度简单1057收藏分享切换为英文关注反馈 判断一个整数是否是回文数 回文数是指正序 xff08 从左向右 xff09 和倒序 xff08 从右向左 xff09 读都是一样的整数 示例 1 输入 121 输出 true 示例 2
  • C++11 并发指南一(C++11 多线程初探)

    相信 Linux 程序员都用过 Pthread 但有了 C 43 43 11 的 std thread 以后 xff0c 你可以在语言层面编写多线程程序了 xff0c 直接的好处就是多线程程序的可移植性得到了很大的提高 xff0c 所以作为
  • 二分搜索binary search和贪婪算法

    二分搜索binary search 定义 xff1a 二分搜索也称折半搜索 xff0c 是一种在有序数组中查找某一特定元素的搜索算法 运用前提 xff1a 必须是排好序的 输入并不一定是数组 xff0c 也可能是给定一个区间和终止的位置 优
  • 面试题52. 两个链表的第一个公共节点

    面试题52 两个链表的第一个公共节点 难度简单51收藏分享切换为英文关注反馈 输入两个链表 xff0c 找出它们的第一个公共节点 如下面的两个链表 xff1a 在节点 c1 开始相交 示例 1 xff1a 输入 xff1a intersec
  • 求解两个字符串的最长公共子序列

    一 xff0c 问题描述 给定两个字符串 xff0c 求解这两个字符串的最长公共子序列 xff08 Longest Common Sequence xff09 比如字符串1 xff1a BDCABA xff1b 字符串2 xff1a ABC
  • leetcode刷题6.16 树的层序遍历,树的序列化

    给你一个二叉树 xff0c 请你返回其按 层序遍历 得到的节点值 xff08 即逐层地 xff0c 从左到右访问所有节点 xff09 示例 xff1a 二叉树 xff1a 3 9 20 null null 15 7 3 9 20 15 7
  • 深度优先及广度优先算法

    深度优先搜索算法DFS 广度优先搜索算法BFS 在猪呢个算法知识点中占比非常大 xff0c 应用最多的地方是对图进行遍历 xff08 树以为是图的一种 xff09 深度优先搜索算法DFS DFS解决的 是连通性的问题 xff0c 及给定两个
  • 厄拉多塞筛法 快速求质数 /回文子串

    西元前250年 xff0c 希腊数学家厄拉多塞 Eeatosthese 想到了一个非常美妙的质数筛法 xff0c 减少了逐一检查每个数的的步骤 xff0c 可以比较简单的从一大堆数字之中 xff0c 筛选出质数来 xff0c 这方法被称作厄
  • Docker部署Sonarqube

    1 下载镜像 docker pull registry span class token punctuation span cn span class token operator span shenzhen span class toke
  • leetcode刷题 旋转链表

    92 反转链表 II 难度中等393 反转从位置 m 到 n 的链表 请使用一趟扫描完成反转 说明 1 m n 链表长度 示例 输入 1 gt 2 gt 3 gt 4 gt 5 gt NULL m 61 2 n 61 4 输出 1 gt 4
  • 分布式实时处理系统——C++高性能编程 RAII resource acquisition is initialization

    分布式实时处理系统 C 43 43 高性能编程 前言 基于通信基础 xff0c 介绍Hurricane实时处理系统的工程实现 xff0c 主要使用C 43 43 语言 一 IPC socket 异步I O epoll 二 C 43 43 1
  • 6月21 刷题思考

    1 RALL相关知识点 2 std set的使用 xff1f xff1f 不熟练 3 一个无序整数数组中找到最长连续序列 4 Two Sum 问题 Data structure design 5 i 43 43 在两个线程里边分别执行100
  • V2X就是Vehicle To Everything 国标中有五种消息BSM、RSI、RSM、SPAT、MAP

    前面讲到V2X就是Vehicle To Everything xff0c 即车队外界所有信息的交换 xff0c 这里的X代表Everything xff0c 在V2X概念中 xff0c 我们将它看作四大部分 xff0c 车与车通信 xff0
  • 6月23 leetcode 二进制求和

    67 二进制求和 难度简单404收藏分享切换为英文关注反馈 给你两个二进制字符串 xff0c 返回它们的和 xff08 用二进制表示 xff09 输入为 非空 字符串且只包含数字 1 和 0 示例 1 输入 a 61 34 11 34 b
  • 利用栈实现树的中序遍历

    94 二叉树的中序遍历 难度中等537收藏分享切换为英文关注反馈 给定一个二叉树 xff0c 返回它的中序 遍历 示例 输入 1 null 2 3 1 2 3 输出 1 3 2 进阶 递归算法很简单 xff0c 你可以通过迭代算法完成吗 x
  • STL中的set详解

    1 关于set C 43 43 STL 之所以得到广泛的赞誉 xff0c 也被很多人使用 xff0c 不只是提供了像vector string list等方便的容器 xff0c 更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构