STL主要分成三类:
algorithm(算法) : 对数据进行处理(解决问题)步骤的集合方式;
container(容器) : 用来管理许多不用类型数据元素的集合体;
iterator(迭代器) : 可遍历容器内的全部或者一部分的元素的对象;
使用STL的头文件有:
<algorithm> <functional> <iterator> <verctor> <deque> <list>
<map> <memory> <numeric> <queue> <set> <stack> <utility>
算法 : 在STL中已经实现了的,我们只需要去调用接口就可以达到需要完成的 目的, 不需要去自己再实现算法,除非我们需要实现某些特定的算法时可以自己加入一些算法;
容器 : 分为以下几种 :
vector : (单端数组)
使用时要加 <vector> 头文件
它是将元素置于一个动态数组种加以管理的容器, 可以随机存取元素
支持索引值直接存取,用[]操作符或at()方法对元素进行操作;
vector使用方法:
对象构造:
vector<T>v_T; T为数据类型,如(int,float,string,指针和类);
带参数构造: vector(v_T.begin(),v_T.end()); 构造函数将begin(),(元素最开始的值)
end(),(元素最后一个值+1的位置)中的元素拷贝给本身,
注意: 该区间是左闭右开的区间;
vector(n,elem); 构造函数将 n 个elem(元素)拷贝给本身;
vector(const vector&v1); 拷贝构造函数
赋值:
v1.assign(2,66); 改变vector中的元素的个数和值;
v1.assign(v2.degin(),v2.end());使用迭代器重新赋值
int demo[]={1,2,3,4};
v2.assign(demo,demo+3); 使用指针赋值
v1=v2; 赋值运算
vector的大小:
v1.size(); 返回容器中元素的个数;
v1.empty(); 判断容器是否为空;
v1.resize(num); 重新指定容器的长度为num,若容器变长,则以默认值填充新位置,
如果容器变短,则末尾超出容器长度的元素被删除掉;
v1.resize(num,elem); 重新指定容器的长度为num,容器变长,则以elem值填充新
位置,变短则末尾超出容器长度的元素被删除掉;
vector末尾添加和移除操作
v1.push_back(1); 在容器尾部加入一个元素,该元素对应的值为1;
v1.pop_back(); 移除容器中最后一个元素
vector数据存取
使用下标操作 v1[0]=100;
使用at 方法 v1.at(0)=100;
接口返回的引用 v1.front()和 v1.back();
注意: 第一和第二种方式必须注意越界;
vector元素插入
v1.insert(pos,elem); 在pos位置插入一个elem元素的拷贝,返回新数据的位置;
v1.insert(pos,n,elem);在pos位置插入n个elem数据,无返回值;
v1.insert(pos,begin(),end());在pos位置插入(begin(),end())区间的数据,无返回值
vector元素删除
v1.clear(); 删除所以元素;
v1.erase(v1.brgin()+1); 删除某个位置的单个元素
v1.erase(v1.begin(),v1.begin()+3);删除begin()到begin+3这个区间的所以元素
deque: (双端数组)
使用时加 <deque> 头文件
特点: 在接口上和vector非常相似,在许多操作的地方可以直接替换;
可以随机存取元素(支持索引值直接存取,用[]操作符或at()方法);
头部和尾部添加或删除元素都非常快,但是在中部插入或移除元素就比较费时;
deque对象构造和vector一样,没有差异;
deque<T>v1;
deque元素的添加和移除操作
v1.push_back(elem); 容器尾部添加一个数据;
v1.push_front(elem); 容器头部插入一个数据;
v1.pop_back(); 删除容器最后一个数据;
v1.pop_front(); 删除容器第一个数据;
deque的数据存取和vector一样;就是在头部插入数据时没有vector费时;
deque与迭代器
v1.begin(); 返回容器中第一个元素的迭代器;
v1.end(); 返回容器中最后一个元素+1的位置的迭代器;
v1.rbegin();返回容器中倒数第一个元素的迭代器;
v1.rend(); 返回容器中倒数最后一个元素+1的位置的迭代器;
v1.cbegin();返回容器中第一个元素常量的迭代器;
v1.cend(); 返回容器中最后一个元素+1的位置的常量迭代器;
普通迭代器遍历
for(deque<int>::iterator it=v1.begin();it!=v1.end();++it){
(*it)++;//迭代器指针需要先解引再进行++;
cout<<*it<<endl;
}
常量迭代器
for(deque<int>::const_iterator it=v1.cbegin();it!=v1.cend();++it){
cout<<*it<<endl;
}
逆转迭代器
for(deque<int>::reverse_iterator it=v1.rbegin();it!=v1.rend();++it){
cout<<*it<<endl;
}
deque赋值
v1.assign(begin(),end()); 将(begin(),end())区间的数据拷贝赋值
本身,注意该区间是左闭右开的区间;
v1.assign(n,elem);将n个elem拷贝赋值给本身;
v1&opertor=(const v1&deq);重载赋值给本身;
v1.swap(v2);将v1和v2的元素互换;
deque的大小的使用方法和vector一样的;
deque的插入方式和vector一样;
deque的删除方式和vector一样;
list: 双向链表容器,可以高效地进行插入删除元素
需要 <list> 头文件
特点:
list不可以随机存取元素,所以不支持at.(pos)函数与[]操作符,可以对其迭代器
执行++,但是不可以这样操作迭代器: it+3;
list对象的构造和vector一样;
list头尾添加和移除元素操作和deque一样;
list数据存取
v1.front(); 返回第一个元素;
v1.back(); 返回最后一个元素;
list的迭代器和deque一样;
list的赋值和deque一样;
list的大小操作和deque一样;
list的插入和vector一样;
list删除元素:
v1.clear(); 移除容器所有数据
v1.erase(begin(),end()); 删除(begin(),end())区间的数据,返回
下一个数据的位置;
v1.erase(pos); 删除pos位置的数据,返回下一个数据的位置;
v1.remove(elem); 删除容器中所有与elem值匹配的元素;
list的反序排列
v1.reverse();//反转链表,如1,2,3运行次方法后:3,2,1;
C++11新特性: 变参模板,完美转发和emplace(队列)
变参模板: 使得emplace可以接受任意参数,这样就可以适用于任何对象的
构造;
完美转发: 使得接收下来的参数,能够原样的传递给对象的构造函数,这带来
另一个方便性;
set和multiset容器(红黑树变体的数据结构所实现的容器)
需要头文件<set>
set和multiset是一个集合容器,其中set所包含的元素是唯一的,集合中的元
素按一定的顺序排列,set采用红黑树变体的数据结构实现,红黑树属于平
衡二叉树,在插入和删除操作上比vector快,在n个数中查找目标数的效率
是log2n;
红黑树定义: 每个节点都带有属性(红色或黑色)的自平衡二叉查找树;
满足下列性质:
节点是红色或黑色;
根节点是黑色;
所有叶子节点都是黑色节点(NULL);
每个红色节点必须有两个黑色子节点,(从根到叶子的节点上不能有
两个连续的红色节点);
从任一节点到其每个叶子的所有简单路径都包含相同的黑色节点;
set和multiset的特点:
set中元素插入过程是按排序规则插入,所以不能指定插入位置;
set不可以直接存取元素,(不可以使用at(pos)和[]操作符);
set和multiset的区别在于:set支持唯一的键值,每个元素只可以出现一次;而
multiset中同样的值可以出现多次;
不可以直接修改set和multiset容器中的元素值,因为该类容器是自动排序的;
如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素;
set和multiset对象的构造和list一样;
仿函数(函数对象) function 头文件<functional>
尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数
的方法,就是函数对象;
function,翻译成函数对象,伪函数,它是重载了"()"操作符的普通类对象,从语法
上讲,它与普通函数类型一样;
<functional>头文件中包含的greater<>与less<>就是函数对象;
greater<>和less<>的简单实现原理:
struct greater{
bool operator()(const int&left,const int&right){
return(left>right);
}
}
struct less{
bool operator()(const int&left,const int&right){
return(left<right);
}
}
set和multiset容器就是调用函数对象的operator()方法比较两个值的大小;
set和multiset的插入和pair的用法:
pair表示一个对组,它将两个值视为一个单元,把两个值捆绑在一起;
pair<T1,T2>用来存放的两个值的类型,可以不一样,也可以一样,如T1为int,
T2为string, T1,T2也可以是自定义类;
pair.first是pair里面的第一个值,是T1类型;
pair.second是pair里面的第二个值,是T2类型;
set和multiset的迭代器和list的迭代器一样;
set和multiset的大小和list一样,
注意: 它们没有resize的方法;
set和multiset的删除和list的方式一样;
删除区间内的某个或某些元素
setInt是用set<int>声明的容器,假设它内部现已包含按顺序的1, 2, 3, 4元素
set<int>::iterator itBegin=setInt.begin();
++ itBegin;
set<int>::iterator itEnd=setInt.begin();
++ itEnd;
++ itEnd;
++ itEnd;
setInt.erase(itBegin,itEnd);
//此时容器setInt包含按顺序的1, 4, 5, 6四个元素。
删除容器中第一个元素
setInt.erase(setInt.begin()); //4, 5, 6
删除容器中值为5的元素
setInt.erase(5); //4, 6
删除setInt的所有元素
setInt.clear(); //容器为空
set和multiset的查找
v1.find(elem); 查找elem元素,返回指向elem元素的迭代器
v1.count(elem);返回容器中值为elem的元素个数,对set来说,要么是0,要么是1
对multiset来说,值可能大于1;
v1.lower_bound(elem); 返回第一个>=elem元素的迭代器;
v1.upper_bound(elem); 返回第一个>elem元素的迭代器;
v1.equal_range(elem); 返回容器中与elem相等的上下限的两个迭代器,上限是
闭区间,下限是开区间,如(begin(),end()),以上函数
返回两个迭代器,而这两个迭代器被封装在pair中;
map和multimap容器(关联式容器,如:储物柜)
需要头文件<map>
map是标准的关联式容器,一个map里存储的元素是一个键值对序列,叫
(key,value)键值对,它提供基于key快速检索数据的能力;
map中的key值是唯一的,集合中的元素按一定的顺序排列,元素插入过程
是按排序规则插入,所以不能指定插入的位置;
map底层的具体实现是采用红黑树变体的平衡二叉树的数据结构,在插
入,删除和检索操作上比vector快很多;
map可以直接存取key所对应的value,支持[]操作符, 如:map[key]=value;
map和multimap的区别在于:
map支持唯一键值,每个键只能出现一次,而multimap中相同键可以
出现多次,multimap不支持[]操作符;
map/multimap对象的构造和set一样;
map<T1,T2>v1;T1和T2可以是各种数据类型,指针类型和自定义类型
map的插入与迭代器
v1.insert(...); 往容器插入元素,返回pair<iterator,bool>
map中插入元素的方式有:
map<int,string>mapStu;
方式一、通过pair的方式插入对象
mapStu.insert( pair<int,string>(1,"张三") );
方式二、通过pair的方式插入对象
mapStu.inset(make_pair(2, “李四”));
方式三、通过value_type的方式插入对象
mapStu.insert( map<int,string>::value_type(3,"王五") );
方式四、通过数组的方式插入值
mapStu[4] = "赵六";
mapStu[5] = “陈七";
注意:
前三种方式,采用的是insert()方式,该方式返回值为pair<iterator,bool>
第四种方式非常直观,但遇到相同的键时会进行覆盖操作,如: 插入key
为4的键值时,先在mapStu中查找主键为4的项,若没有,则将一个键生
成为4,值为默认初始化值的对组插入到mapStu中,然后再将值改成
"赵六",若发现已经存在4这个键,则修改这个键对应的value;
string strName=mapStu[8];//取值操作或插入操作
只有当mapStu存在8这个键时才是正确的取值操作,否则会自动插入一
个实例,键值为8,值为默认构造时的初始值;
map/multimap的迭代器和deque一样;
map/multimap 排序:
map<T1,T2,less<T1> > mapA; //该容器是按键的升序方式排列元素。未指定函数对象,默认采用less<T1>函数对象。
map<T1,T2,greater<T1>> mapB; //该容器是按键的降序方式排列元素。
less<T1>与greater<T1> 可以替换成其它的函数对象functor。可编写自定义函数对象以进行自定义类型的比较,使用方法与set构造时所用的函数对象一样。
map对象的拷贝构造与赋值
map(const map &mp); //拷贝构造函数
map& operator=(const map &mp); //重载等号操作符
map.swap(mp); //交换两个集合容器
map/multimap的大小和set一样;
map的删除:
map.clear(); //删除所有元素
map.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
map.erase(beg,end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
map.erase(key); //删除容器中key为key的对组,返回删除的对组个数
map.erase(key_type *first, key_type *last) //删除数组指定的半闭半开的区间中特定的key对应的所有队组
map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(1, "张三"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
//删除区间内的元素,迭代器指示区间(半闭半开)
map<int,string>::iterator itBegin=mapA.begin();
++ itBegin;
map<int,string>::iterator itEnd=mapA.end();
mapA.erase(itBegin,itEnd); //此时容器mapA仅仅包含{1,"张三"}一个元素。
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
//删除容器中的第一个元素,使用迭代器指示位置
mapA.erase(mapA.begin()); //mapA包含{2,"李四"}{3,"王五"}{4,"赵六"}三个元素
//删除容器中key为4的元素
mapA.erase(4);
//删除mapA的所有元素
mapA.clear(); //容器为空
map/multimap的查找:
map.find(key); 查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
map.count(key); //返回容器中键值为key的对组个数。对map来说,要么是0,要么是1;对multimap来说,值>=0。
map.lower_bound(keyElem); //返回第一个key>=keyElem元素的迭代器。
map.upper_bound(keyElem); // 返回第一个key>keyElem元素的迭代器。
map.equal_range(keyElem); //返回容器中key与keyElem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。
queue(队列容器)
queue的队列容器,是一种"先进先出"的容器;
默认情况下queue是利用deque容器实现的一种容器;
它只允许在队列的前端(front)进行删除操作,而在队列的后端(back)进
行插入操作;
需要头文件<queue>
queue的对象构造和vector一样;
queue 对象的带参构造
queue<int, list<int>> queueList; //内部使用list 来存储队列元素的queue 容器.
错误: queue<int, vector<int>> queueList; //内部不能使用vector来存储队列元素
queue的push()与pop()方法
queue.push(elem); //往队尾添加元素
queue.pop(); //从队头处移除队首元素
queue对象的拷贝构造与赋值
queue(const queue &que); //拷贝构造函数
queue& operator=(const queue &que); //重载等号操作符
queue的数据存取
queue.back(); //返回最后一个元素
queue.front(); //返回第一个元素
queue的大小使用方式和vector一样;
优先级队列priority_queue
优先队列: 它的入队顺序没有变化,但是出队的顺序是根据优先级的高低来决定的,优先
级高的优先出队;
最大值优先级队列,最小值优先级队列;
用来开发一些特殊的应用;
stack: (堆栈容器) 头文件
stack是堆栈容器,是一种"先进后出"的容器
//stack是基于deque容器而实现的容器。
#include <stack>
stack对象的默认构造
stack采用模板类实现, stack对象的默认构造形式: stack <T> stkT;
stack <int> stkInt; //一个存放int的stack容器。
stack <float> stkFloat; //一个存放float的stack容器。
stack <string> stkString; //一个存放string的stack容器。
...
//尖括号内还可以设置指针类型或自定义类型。
stack的push()与pop()方法
stack.push(elem); //往栈头添加元素
stack.pop(); //从栈头移除第一个元素
stack<int> stkInt;
stkInt.push(1);
stkInt.push(2);
stkInt.pop();
stkInt.push(3);
此时stkInt存放的元素是1, 3
stack对象的拷贝构造与赋值
stack(const stack &stk); //拷贝构造函数
stack& operator=(const stack &stk); //重载等号操作符
stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(2);
stkIntA.push(3);
stack<int> stkIntB(stkIntA); //拷贝构造
stack<int> stkIntC;
stkIntC = stkIntA; //赋值
stack的数据存取
stack.top(); //返回最后一个压入栈元素
stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(2);
stkIntA.push(3);
int iTop = stkIntA.top(); //3
stkIntA.top() = 88; //88
stack的大小
stack.empty(); //判断堆栈是否为空
stack.size(); //返回堆栈的大小
stack<int> stkInt;
stkInt.push(1);
stkInt.push(2);
stkInt.push(3);
int iSize = stkInt.size(); //3
array容器(C++11新增)
array容器概念
1.array是将元素置于一个固定数组中加以管理的容器。
2.array可以随机存取元素,支持索引值直接存取, 用[]操作符或at()方法对元素进行操作,也可以使用迭代器访问
3.不支持动态的新增删除操作
4.array可以完全替代C语言中的数组,使操作数组元素更加安全!
array对象的构造
1.array采用模板类实现,array对象的默认构造形式(涉及非类型参数-数值类模板)
2.array<T,10> arrT; //10 为数值型模板参数
array<int, 6> a1; //一个存放int的array容器
array<float, 6> a2; //一个存放float的array容器
array<student, 6> a3; //一个存放student的array容器
array<int, 6> a1={1,2,3,4,5,6}; //定义时同时初始化
array的赋值
a2.assign(0);//第一种玩法 改变原来array中的所有元素的值
array<int, 6> a1 = {1, 2, 3};
array<int, 6> a2 ;
a2 = a1; //第二种玩法 赋值运算,将a1 赋值给a2
array的大小
array.size(); //返回容器中元素的个数
array.max_size(); //返回容器中最大的元素个数,与size 等同
array.empty(); //判断容器是否为空
array的数据存取
第一 使用下标操作 a1[0] = 100;
第二 使用at 方法 如: a1.at(2) = 100;
第三 接口返回的引用 a2.front() 和 a2.back()
第四 返回内建数组的指针 a1.data()
注意: 第一和第二种方式必须注意越界
注意:任何时候在模板(template)中使用一个嵌套从属类型名称, 需要在前一个位 置, 添加关键字typename;
比如上例中使用迭代器类型时,就要使用typename.虽然在vs2010 和vs2015中没有错误,但在VC++2019和gcc编译器中,都会报错。