STL
STL从广义上分为三类:algorithm(算法),container(容器),iterator(迭代器);容器和算法都通过迭代器无缝衔接。C++标准中,STL被组织为13个头文件:<algorithm>
, <deque>
,<functional>
,<iterator>
,<vector>
.<list>
,<map>
,<memory>
,<numeric>
,<queue>
,<set>
,<stack>
和<utility>
。
STL的特点:
- STL是c++的一部分,内置在编译器中;
- 数据结构与算法的分离;
- 算法可以操作相应的容器;
- 具有高可重用性,高性能,高移植性,跨平台的优点:
- 高可重用性:STL都采用模板类和模板函数的方式实现;
- 高性能:
- 高移植性:
- 跨平台:
容器
容器的分类
- 序列式容器:
- 每个元素都有固定的位置,取决于插入的时机和地点,与元素值无关;
- vector,deque,list,stack,queue
- 关联式容器:
- 元素的位置取决于特定的排序准则,和插入顺序无关;
- set,multiset,map,multimap
以下是一个表格,列出了STL标准模板库中的一些主要容器及其特点:
容器类型 | 特点 |
---|---|
vector | 1. 动态数组,连续存储 |
2. 随机访问元素,时间复杂度为O(1) | |
3. 末尾添加或删除元素效率高,中间或开头操作效率低 | |
4. 内存自动管理,但可通过reserve()优化 | |
5. 迭代器可能因内存重新分配而失效 | |
list | 1. 双链表,元素存储非连续 |
2. 在序列任意位置进行插入和删除操作效率高 | |
3. 访问元素需从头或尾开始遍历,时间复杂度为O(n) | |
deque | 1. 双端队列,由多个固定大小的块组成 |
2. 在序列头部和尾部添加或删除元素效率高 | |
3. 随机访问元素,时间复杂度为O(1) | |
set | 1. 关联容器,通过红黑树实现 |
2. 元素唯一,自动排序 | |
3. 插入、删除和查找操作的时间复杂度为O(log n) | |
multiset | 1. 允许重复元素的set |
2. 与set类似的操作特性 | |
map | 1. 关联容器,存储键值对 |
2. 键唯一,自动排序 | |
3. 插入、删除和查找操作的时间复杂度为O(log n) | |
multimap | 1. 允许重复键的map |
2. 与map类似的操作特性 | |
stack | 1. 后进先出(LIFO)的容器适配器 |
2. 基于其他容器(如deque或vector)实现 | |
queue | 1. 先进先出(FIFO)的容器适配器 |
2. 基于其他容器(如deque或list)实现 | |
priority_queue | 1. 最大堆或最小堆实现的容器适配器 |
2. 元素按优先级排序,默认最大堆 | |
unordered_map | 1. 哈希表实现的关联容器 |
2. 存储键值对,键唯一 | |
3. 插入、删除和查找操作的平均时间复杂度为O(1) | |
unordered_multimap | 1. 允许重复键的unordered_map |
2. 与unordered_map类似的操作特性 | |
unordered_set | 1. 哈希表实现的关联容器 |
2. 元素唯一 | |
3. 插入、删除和查找操作的平均时间复杂度为O(1) | |
unordered_multiset | 1. 允许重复元素的unordered_set |
2. 与unordered_set类似的操作特性 |
vector容器
-
vector容器简介:
- 是一个将元素置于动态数组中加以管理的容器;
- 可以随机存取元素(支持索引值直接存取,用[]操作符或at()方法);
- 在尾部添加或移除元素速度快。
-
vector对象的构造函数
采用模板类实现,vector对象的默认构造形式vector<T> vecT;
例如:
vector<int> vecInt; //存放int的vector容器
vector<float> vecFloat;
class CA{};
vector<CA*> vecpCA; //存放CA对象的指针的容器
vector<CA> vecCA; //存放CA对象,由于容器元素的存放是深拷贝,因此CA必须提供CA的拷贝构造函数,以保证CA对象间拷贝正常。
- vector对象的带参数构造
理论知识:vector(beg,end);
:构造函数将beg和end指针间的元素拷贝给本身进行初始化,该区间为左闭右开区间。vector(n, elem);
:构造函数将n个elem拷贝给本身。vector(const vector &vec);
:拷贝构造函数。
int arr[5]={1,2,3,4,5};
vector<int> vecInt2(arr, arr+5);
vector<int> vecNum(5, 10);
vector<int> vecCopy(vecInt2);
-
vector的赋值
理论知识:vector.assign(beg, end);
:将beg到end区间的数据拷贝到vector(左闭右开)。vector.assign(n,elem);
:将n个elem拷贝赋值给本身。vector& operator=(const vector &vec);
:重载等号操作符。vector.swap(vec);
:将vec与本身的元素互换。
-
vector的大小
理论知识:vector.size();
:返回容器中元素的个数。vector.empty();
:判断容器内是否为空,返回值为布尔类型。vector.resize(num);
:重新指定大小,若容器变大,则以默认值(0)填充新位置,若变小,则删除多余元素。vector.resize(num,elem);
:重新指定容器的长度为num,以elem填充新位置。- vector末尾的添加移除操作:
- vector vecInt;
- vecInt.push_back(3); //在容器尾部加入元素3;
- vecInt.push_back(10); //在容器尾部加入元素10;
- vecInt.pop_back(); //从末尾删除一个元素;
- vecInt.erase(itInt); //假设itInt是一个迭代器,删除指定位置的元素;
int arr[5]={1,2,3,4,5};
vector<int> vecInt;
cout<<"vecIntSize="<<vecInt.size()<<endl;
if (vecInt.empty()){
cout<<"vecInt is empty!"<<endl;
}
vecInt.assign(arr,arr+5);
vecInt.resize(4);
vecInt.resize(10,10);
cout<<"-----------------------------------------"<<endl;
cout<<"vecIntSize="<<vecInt.size()<<endl;
if (vecInt.empty()){
cout<<"vecInt is empty!"<<endl;
}
for(int i=0;i<vecInt.size();i++){
cout<<vecInt.at(i)<<"";
}
cout<<endl;
-
vector元素的数据存取
理论知识:vec.at(idx);
:返回索引为idx所指的数据,如果idx越界,抛出out_of_range异常。vec[idx];
:返回索引idx所指的数据,越界时,运行直接报错。
-
vector的插入insert函数的使用
理论知识:vector.insert(pos,elem);
:在pos位置插入一个元素的拷贝,返回新数据的地址 (新的迭代器的值)。vector.insert(pos,num,elem);
:在pos位置插入n个elem数据,无返回值。vector.insert(pos,beg,end);
:在pos位置插入一个区间的数据,无返回值。
-
vector容器中迭代器的使用
vector<int>::iterator iterVecInt;
:vector容器的迭代器属于“随机访问迭代器”,即迭代器一次可以移动多个位置。
下面是一些常用的与迭代器相关的操作:
操作 | 描述 | 实例 |
---|---|---|
iterator begin() | 返回指向容器第一个元素的迭代器 | vector<int>::iterator itInt = vecInt.begin(); |
const_iterator begin() const | 返回指向容器第一个元素的常量迭代器 | |
iterator end() | 返回指向容器“尾部之后”的迭代器(不指向任何有效元素) | |
const_iterator end() const | 返回指向容器“尾部之后”的常量迭代器(不指向任何有效元素) | |
iterator rbegin() | 返回指向容器最后一个元素的反向迭代器 | |
const_iterator rbegin() const | 返回指向容器最后一个元素的常量反向迭代器 | |
iterator rend() | 返回指向容器“开头之前”的反向迭代器(不指向任何有效元素) | |
const_iterator rend() const | 返回指向容器“开头之前”的常量反向迭代器(不指向任何有效元素) | |
iterator ++it 或 it++ | 迭代器前移,指向下一个元素 | |
iterator --it 或 it-- | 迭代器后移,指向前一个元素 | |
it1 == it2 | 比较两个迭代器是否相等(指向同一位置) | |
it1 != it2 | 比较两个迭代器是否不相等 | |
*it | 解引用迭代器,获取其指向的元素的值 | |
it + n | 返回迭代器向前移动n个位置后的迭代器 | |
it - n | 返回迭代器向后移动n个位置后的迭代器 | |
it1 - it2 | 返回两个迭代器之间的距离(指向的元素数量差) | |
it1 < it2 | 比较两个迭代器,判断it1是否在it2之前 | |
it1 <= it2 | 比较两个迭代器,判断it1是否在it2之前或与其相等 | |
it1 > it2 | 比较两个迭代器,判断it1是否在it2之后 | |
it1 >= it2 | 比较两个迭代器,判断it1是否在it2之后或与其相等 |
通过循环的方式使用迭代器遍历vecInt中的所有元素:
//循环迭代器遍历元素
for (vector<int>::iterator itInt=vecInt.begin();itInt!=vecInt.end();itInt++)
cout<<*itInt<<" ";
cout<<endl;
- 迭代器失效
在使用insert()插入一个元素时,在没有初始化空间的前提下使用迭代器在最后一个元素的位置上插入元素,会导致迭代器失效,这是因此在插入元素后可能会导致初始容器的内存被释放,而其中的元素会被隐形搬迁至一个新的内存段。(可以看作迭代器变成了一个野指针)
因此如上所说(见vector的插入部分),vector.insert(pos, elem);
会返回一个新的迭代器的值。