C++ STL库
C++ STL库总结
一.STL库介绍
STL 是“Standard Template Library”的缩写,中文译为“标准模板库”。STL 是 C++ 标准库的一部分,不用单独安装。
C++ 对模板(Template)支持得很好,STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。
-
STL库和C++标准库的关系

(图片来自网络)
-
STL六大组件

(图片来自网络)
六大组件的交互关系:container(容器) 通过 allocator(配置器) 取得数据储存空间,algorithm(算法)通过 iterator(迭代器)存取 container(容器) 内容,functor(仿函数) 可以协助 algorithm(算法) 完成不同的策略变化,adapter(配接器) 可以修饰或套接 functor(仿函数)。
二.常用STL库
-
vector
vector 的中文名为向量,可以理解为一个序列容器,里面存放的是相同的数据结构类型,类似于数组但与数组又有微妙的不同。
vector 采用的是连续动态的空间来存储数据,它是动态的数组,它不仅可以使用下标访问每一个位置的数据,还可以对它的长度进行改变,十分的灵活。但是它的灵活也是有代价的,我们都知道,数组在内存中的长度是开始声明的时候就已经确定好了,要改变长度,就意味着需要重新的去申请空间,再将需要的元素移动到这个空间中,在释放原来的空间,但是 vector 对内存的处理或许并不是这样的,无论我们加多少个值或是删除多少个值来改变 vector 的 size,vector 的头地址始终是不变的,这可能和它的内部实现有关,在这里我也不追究了。
vector 同时也有许多的函数方法可以调用,方便我们的数据处理以及内存空间的管理。
vector 与其他的序列容器相比 (list, forward_lists, deque 等),vector 适合于随机的访问元素,在需要很多随机增删操作的情况下,或许 vector 就没有那么的好用了。
vector 在大多数的情况下还是作为动态来使用,一般用来求解线性的题目。
要先包含头文件:#include<vector>
-
-
实例化
-
1 vector<int>v1;//实例化一个存放 int 型数据的 vector,其 size() 为 0,初始值为 0;
2 vector<int>v2(5,1);//实例化一个 size() 为 5 的 vector,且数据的初始值为 1;
3 vector<int>v3(v2);//实例化一个与 v2 相同的 vector,而且可以使用迭代器或数值来确定一个范围。
4 vector<int>v3 = v2;//同上
5 //如果代码中包含了 using namespace std; 则可以不写 std:: 前缀。
-
-
一些操作
-
1 v[0],v[2],v[3];//用下标访问
2 v.at(0),v.at(3);//使用at访问
3 v.front();//访问头元素
4 v.back();//访问最后一个元素
5 int *p=v.data()//返回一个指向这个数组的指针
6 v.size();//返回v的大小
7 v.max_size();//返回vector的最大容量
8 v.capacity();//返回v在内存中的真实大小
9 v.resize(int);//重新定义v的大小
10 v.empty();//判断vector是否为空,返回一个bool值
11 v.push_back(val);//末尾添加元素
12 v.pop_back();//删除末尾的元素
13 v.insert(val);//任意位置插入一个元素
14 v.erase();//任意一个位置删除元素
15 v1.swap(v2)//交换两个vector里面的元素,返回空值
16 v.assign(5,3);//多元素赋值,表示初始化为5个值为3的元素
17 v.clear();//清空vector
18 v.emplace_back();//末尾添加元素,相当于push_back(),但比它更有效率
19 v.emplace();//任意位置插入元素,相当于insert(),但比它更有效率
emplace_back() 能够通过参数构造对象,不需要拷贝或者移动内存,相比于 push_back() 能更好地避免内存的拷贝与移动,使容器插入元素的性能得到进一步提升。由此,在大多数情况下应该优先使用 emplace_back() 来代替 push_back()。
-
-
迭代器和常用算法
-
1 v.begin();//开始位置 2 v.end();//末尾位置 3 v.cbegin();//指向常量的开始位置,意思是不能通过这个迭代器来修改所指的内容(C++11) 4 v.cend();//指向常量的末尾位置,但可以通过其他方式修改的,而且迭代器也是可以移动的(C++11) 5 6 sort(v1.begin(),v1.end());//从小到大排序 7 sort(v1.begin(),v1.end(),Comp);//从大到小排序 8 copy(v1.begin(),v1.end(),v2.begin()+1);//把从 v1[0] 到 v1[size()-1] 的数据复制到 v2[1] 中 9 find(v1.begin(),v1.end(),10);//在 v1[0] 到 v1[size()-1] 中寻找值为 10 的数
-
queue
先包含头文件:#include<queue> 无迭代器
-
- 主要操作
1 queue<int>q;//定义队列,还可以是float,char等类型 2 q.push(item);//将item压入队列尾部 3 q.pop();//删除队首元素,但不返回 4 q.front();//返回队首元素,但不删除 5 q.back();//返回队尾元素,但不删除 6 q.size();//返回队列中元素的个数 7 q.empty();//检查队列是否为空,如果为空返回true,否则返回false
-
stack
先包含头文件:#include<stack> 无迭代器
-
- 主要操作
1 satck<int>s;//定义栈,还可以是float,char等 2 s.push(item);//将item压入栈顶 3 s.pop();//删除栈顶的元素,但不会返回 4 s.top();//返回栈顶的元素,但不会删除 5 s.size();//返回栈中元素的个数 6 s.empty();//检查栈是否为空,如果为空返回true,否则返回false
-
map
Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改key
先包含头文件:#include<map>
-
-
定义
-
1 map<int,string>mp;//定义一个用int作为索引,并拥有相关联的指向string的指针 2 map<string,int> mp;//如果是字符串到int的映射,必须使用string不能使用char数组
-
- 元素的插入
1 //第一种:使用insert函数插入pair数据 2 map<int, string> mapStudent; 3 mapStudent.insert(pair<int, string>(1, "student_one")); 4 mapStudent.insert(pair<int, string>(2, "student_two")); 5 mapStudent.insert(pair<int, string>(3, "student_three")); 6 7 //第二种:使用insert函数插入value_type数据 8 map<int, string> mapStudent; 9 mapStudent.insert(map<int, string>::value_type(1,"student_one")); 10 mapStudent.insert(map<int, string>::value_type (2"student_two")); 11 mapStudent.insert(map<int,string>::value_type(3,"student_three"); 12 13 //第三种:使用数组的方式插入数据 14 map<int, string> mapStudent; 15 mapStudent[1] = "student_one"; 16 mapStudent[2] = "student_two"; 17 mapStudent[3] = "student_three";
以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,当然了第一种和第二种在效果上是完成一样的,用insert函数插入数据,在数据的 插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值。
-
- 常用函数
1 mp.begin();//返回指向map头部的迭代器 2 mp.clear();//删除所有元素,复杂度为O(N) 3 mp.count();//返回指定元素出现的次数 4 mp.empty();//如果map为空则返回true 5 mp.end();//返回指向map末尾的迭代器 6 mp.erase(key);//删除一个元素,时间复杂度为O(logN) 7 mp.find(key);//查找一个元素,返回键为key的映射,时间复杂度为O(logN) 8 mp.insert();//插入元素 9 mp.max_size();//返回可以容纳的最大元素个数 10 mp.size();//返回map中元素的个数 11 mp.swap();//交换两个map
-
- 数据的遍历
1 map<int, string> mapStudent; 2 mapStudent[1] = "student_one"; 3 mapStudent[1] = "student_two"; 4 mapStudent[2] = "student_three"; 5 6 第一种:应用前向迭代器 7 map<int, string>::iterator iter; 8 for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) 9 cout<<iter->first<<' '<<iter->second<<endl; 10 11 第二种:应用反相迭代器 12 map<int, string>::reverse_iterator iter; 13 for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++) 14 cout<<iter->first<<" "<<iter->second<<endl; 15 16 第三种:采用数组的形式 17 int nSize = mapStudent.size(); 18 for(int nindex = 1; nindex <= nSize; nindex++) 19 cout<<mapStudent[nindex]<<endl; 20 //此处应注意,应该是 for(int nindex = 1; nindex <= nSize; nindex++) 21 //而不是 for(int nindex = 0; nindex < nSize; nindex++)
-
- 特性
1.一次存两个数据key和value,以pair的形式存在,key和value一一对应,通过key来操作value。
2.有序容器,对key进行排序。
3.不允许key重复,允许value重复。
4.增效率低,查询效率高。
-
- 常用用途
a、建立字符或者字符串与整数之间的映射的时候,使用map。
b、判断大整数或者其他类型数据是否存在的时候,map可以当bool数组用。
c、字符串和字符串的映射。
-
multimap
- multimap与map的区别:map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
- map和multimap会根据key对元素进行自动排序,所以根据key值搜寻某个元素具有良好的性能,但是如果根据value来搜寻效率就很低了。
-
hash_map
- 虽然hash_map和map都是STL的一部分,但是目前的C++标准(C++11)中只有map而没有hash_map,可以说STL只是部分包含于目前的C++标准中。
- hash_map基于hash table(哈希表)。 哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。
详细参考:ttps://www.cnblogs.com/liushuiyaodao/p/3959153.html,
-
unordered_map
- 在C++11中,unordered_map作为一种关联容器,替代了hash_map,unordered_map的底层实现是hash表,所以被称为无序关联容器。不管是map还是unordered_map都是一种 key-map(value) 映射的容器,提供非常高的查找效率。而空间复杂度方面,hash_map最低,unordered_map次之,map最大。
- 插槽:英文为bucket,又可以翻译成桶。在hash表中,hash函数通常返回一个整型(或无符号整型)元素,对应hash表的数组下标,但是数组类型通常为指针指向一片内存或者是一个链表头,对应许多元素,就像一个桶可以装很多元素,这里称为插槽。
详细参考https://www.cnblogs.com/downey-blog/p/10471875.html,https://www.cnblogs.com/langyao/p/8823092.html
-
set(集合)
- 简单说来 set(集合)里的元素不会有相同元素(也就是说 相同的值不存 )并且存进去会自动排序
- set、map它们的底层都是基于红黑树
- set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。
- 平衡二叉检索树的检索使用中序遍历算法,检索效率高于vector、deque、和list的容器。另外,采用中序遍历算法可将键值由小到大遍历出来,所以,可以理解为平衡二叉检索树在插入元素时,就会自动将元素按键值从小到大的顺序排列。
应先包含头文件:#include<set>
-
- 定义
1 set<int>s;//也可以是float,double,char
-
- 常用函数
1 s.begin();//返回指向容器头部的迭代器 2 s.end();//返回指向容器尾部的迭代器 3 s.rbegin();//等价于s.end() 4 s.rend();//等价于s.begin() 5 s.insert(i);//直接向容器中添加元素 6 s.erase(i);//删除元素i 7 s.erase(s.begin(),s.end());//删除所有元素。等价于s.clear() 8 s.size();//返回当前容器中的元素个数 9 s.max_size(); //返回set容器的最大容量 10 s.count();//判断某元素的出现次数。因为在set中没有重复出现的元素,所以该函数只判断该元素是否存在 11 s.swap();//交换两个set容器中的所有元素 12 s.empty();//如果集合为空,返回true 13 s.find();//返回一个指向被查找到元素的迭代器 14 swap();//交换两个集合变量
-
- 元素遍历
1 set<int>::iterator it; //定义前向迭代器 2 //中序遍历集合中的所有元素 3 for(it = s.begin(); it != s.end(); it++) 4 { 5 cout << *it << " "; 6 }
-
multiset
- set和multiset会根据特定的排序原则将元素排序。两者不同之处在于,multisets允许元素重复,而set不允许重复。
- 和所有的标准关联容器类似,set和multiset通常以平衡二叉树完成。
-
自动排序的主要优点在于使二叉树搜寻元素具有良好的性能,在其搜索函数算法具有对数复杂度。但是自动排序也造成了一个限制,不能直接改变元素值,因为这样会打乱原有的顺序,要改变元素的值,必须先删除旧元素,再插入新元素。所以set和multiset具有以下特点:
- 不提供直接用来存取元素的任何操作元素
- 通过迭代器进行元素的存取。
-
unordered_set
- unordered_set本质是使用hash散列的方式存储数据,是一种使用hash值作为key的容器,所以当有频繁的搜索、插入和移除拥有常数时间。
- 特点:
1、unordered_set是一种容器,它以不特定的顺唯一的元素,并允许根据元素的值快速检索单个元素。
2、在unordered_set中,元素的值同时是唯一标识它的键。键是不可变的,只可增删,不可修改。
3、在内部,unordered_set中的元素没有按照任何特定的顺序排序,而是根据它们的散列值组织成桶(一个线性链表代表一个桶),从而允许通过它们的值直接快速访问单个元素(平均时间复杂度为常数)。
4、unordered_set容器比set容器更快地通过它们的键访问单个元素,尽管它们在元素子集的范围迭代中通常效率较低。
5、容器中的迭代器只能是正向迭代器。
操作和上面类似

浙公网安备 33010602011771号