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、容器中的迭代器只能是正向迭代器。

操作和上面类似

 

posted @ 2022-02-20 11:26  不看春晚好多年  阅读(12)  评论(0)    收藏  举报