STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称。现在主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。STL的从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),容器和算法通过迭代器可以进行无缝地连接。几乎所有的代码都采 用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文件:
、 、 、 、 、 、
使用STL的好处:
1)STL是C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
2)STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分离确实使得STL变得非常通用。例如,在STL的vector容器中,可以放入元素、基础数据类型变量、元素的地址;STL的sort()函数可以用来操作vector,list等容器。
3)程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
4)STL具有高可重用性,高性能,高移植性,跨平台的优点。
(1)一维数组初始化
vector a; //定义了一个名为a的一维数组,数组存储int类型数据
vector b;//定义了一个名为b的一维数组,数组存储double类型数据
vector c;//定义了一个名为c的一维数组,数组存储结构体类型数据,node是结构体类型
指定长度和初始值的初始化:
vector v(n);//定义一个长度为n的数组,初始值默认为0,下标范围[0, n - 1]
vector v(n, 1);//v[0]到v[n-1]所有的元素初始值均为1
//注意:指定数组长度之后(指定长度后的数组就相当于正常的数组了)
列表初始化(多个元素的初始化):
vector a{1, 2, 3, 4, 5};//数组a中有五个元素,数组长度就为5
拷贝初始化:
vector a(n + 1, 0); vector b(a);//两个数组中的类型必须相同,a和b都是长度为n+1,初始值都为0的数组
(2)二维数组初始化
定义第一维固定长度为5,第二维可变化的二维数组
vector v[5];//定义可变长二维数组
//注意:行不可变(只有5行), 而列可变,可以在指定行添加元素
//第一维固定长度为5,第二维长度可以改变
行列均可变的二维数组初始化:
//初始化二维均可变长数组
vector> v;//定义一个行和列均可变的二维数组
行列长度均固定 n + 1行 m + 1列初始值为0:
vector> a(n + 1, vector(m + 1, 0));
(3)方法函数
代码 |
含义 |
c.front() |
返回第一个数据O(1) |
c.back() |
返回数组中的最后一个数据 O(1) |
c.pop_back() |
删除最后一个数据O(1) |
c.push_back(element) |
在尾部加一个数据O(1) |
c.size() |
返回实际数据个数(unsigned类型)O(1) |
c.clear() |
清除元素个数O(N),N为元素个数 |
c.resize(n, v) |
改变数组大小为n,n个空间数值赋为v,如果没有默认赋值为0 |
c.insert(it, x) |
向任意迭代器it插入一个元素x ,O(N) |
c.erase(first,last) |
删除[first,last)的所有元素,O(N) |
c.begin() |
返回首元素的迭代器(通俗来说就是地址)O(1) |
c.end() |
返回最后一个元素后一个位置的迭代器(地址)O(1) |
c.empty() |
判断是否为空,为空返回真,反之返回假O(1) |
(4)排序
使用sort排序要: sort(c.begin(), c.end());
对所有元素进行排序,如果要对指定区间进行排序,可以对sort()里面的参数进行加减改动。
vector a(n + 1);
sort(a.begin() + 1, a.end()); // 对[1, n]区间进行从小到大排序
(5)元素访问
下标法:
//下标访问
for(int i = 0; i < v.size(); i++)
cout << v[i] << " ";
迭代器法:
//方式1
vector::iterator it = vi.begin();
for(int i = 0; i < 5; i++)
cout << *(it + i) << " ";
//方式2
vector::iterator it;
for(it = vi.begin(); it != vi.end();it ++)
cout << *it << " ";
智能指针法(只能遍历完数组,如果要指定的内容进行遍历,需要另选方法。auto 能够自动识别并获取类型):
for(auto val : v)
cout << val << " ";
注意:vi[ i ]和 *(vi.begin() + i) 等价vector和string的STL容器支持*(it + i)的元素访问,其它容器可能也可以支持这种方式访问,但用的不多,可自行尝试。
代码 |
含义 |
s.push(ele) |
元素ele入栈,增加元素O(1) |
s.pop() |
移除栈顶元素O(1) |
s.top() |
取得栈顶元素(但不删除)O(1) |
s.empty() |
检测栈内是否为空,空为真O(1) |
s.size() |
返回栈内元素的个数O(1) |
代码 |
含义 |
front() |
返回队首元素O(1) |
back() |
返回队尾元素O(1) |
push() |
尾部添加一个元素副本进队O(1) |
pop() |
删除第一个元素出队O(1) |
size() |
返回队列中元素个数,返回值类型unsigned int O(1) |
empty() |
判断是否为空,队列为空,返回true O(1) |
代码 |
含义 |
push_back(x) / push_front(x) |
把x插入队尾后 / 队首 O(1) |
back() / front() |
返回队尾 / 队首元素 O(1) |
pop_back() / pop_front() |
删除队尾 / 队首元素 O(1) |
erase(iterator it) |
删除双端队列中的某一个元素 |
erase(iterator first,iterator last) |
删除双端队列中[first,last)中的元素 |
empty() |
判断deque是否空 O(1) |
size() |
返回deque的元素数量 O(1) |
clear() |
清空deque |
(1)排序
//从小到大
sort(d.begin(),d.end())
//从大到小排序
sort(q.begin(), q.end(), greater());//deque里面的类型需要是int型
sort(q.begin(), q.end(), greater());//高版本C++才可以用
优先队列是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。可以实现每次从优先队列中取出的元素都是队列中优先级最大的一个。它的底层是通过堆来实现的。
代码 |
含义 |
q.top() |
访问队首元素 |
q.push() |
入队 |
q.pop() |
堆顶(队首)元素出队 |
q.size() |
队列元素个数 |
q.empty() |
是否为空 |
注意:没有 clear() ! |
不提供该方法 |
优先队列只能通过top()访问队首元素(优先级最高的元素) |
(1)基本数据类型优先级设置
priority_queue pq; // 默认大根堆, 即每次取出的元素是队列中的最大值
priority_queue, greater > q; // 小根堆, 每次取出的元素是队列中的最小值
参数解释:
第二个参数:
vector< int > 是用来承载底层数据结构堆的容器,若优先队列中存放的是double型数据,就要填vector< double >
总之存的是什么类型的数据,就相应的填写对应类型。同时也要改动第三个参数里面的对应类型。
第三个参数:
less< int > 表示数字大的优先级大,堆顶为最大的数字
greater< int >表示数字小的优先级大,堆顶为最小的数字
int代表的是数据类型,也要填优先队列中存储的数据类型
(2)结构体优先级设置
优先级设置可以定义在结构体内进行小于号重载,也可以定义在结构体外。
//要排序的结构体(存储在优先队列里面的)
struct Point {
int x,y;
};
版本一:自定义全局比较规则
//定义的比较结构体
//注意:cmp是个结构体
struct cmp {//自定义堆的排序规则
bool operator()(const Point& a,const Point& b) {
return a.x < b.x;
}
};
//初始化定义
priority_queue, cmp> q; // x大的在堆顶
版本二:直接在结构体里面写
因为是在结构体内部自定义的规则,一旦需要比较结构体,自动调用结构体内部重载运算符规则。结构体内部有两种方式
方式一:
struct node {
int x, y;
friend bool operator < (Point a, Point b) {//为两个结构体参数,结构体调用一定要写上friend
return a.x < b.x;//按x从小到大排,x大的在堆顶
}
};
方式二:
struct node {
int x, y;
bool operator < (const Point &a) const {//直接传入一个参数,不必要写friend
return x < a.x;//按x升序排列,x大的在堆顶
}
};
优先队列的定义:
priority_queue q;
注意: 优先队列自定义排序规则和sort()函数定义cmp函数很相似,但是最后返回的情况是相反的。即相同的符号,最后定义的排列顺序是完全相反的。所以只需要记住sort的排序规则和优先队列的排序规则是相反的就可以了。
(3)特殊数据类型优先级设置
存储pair类型,排序规则:默认先对pair的first进行降序排序,然后再对second降序排序,对first先排序,大的排在前面,如果first元素相同,再对second元素排序,保持大的在前面。
#include
using namespace std;
int main() {
priority_queue>q;
q.push({7, 8});
q.push({7, 9});
q.push(make_pair(8, 7));
while(!q.empty()) {
cout << q.top().first << " " << q.top().second << "\n";
q.pop();
}
return 0;
}
代码 |
含义 |
mp.find(key) |
返回键为key的映射的迭代器O(logN) 注意:用find函数来定位数据出现位置,它返回一个迭代器。当数据存在时,返回数据所在位置的迭代器,数据不存在时,返回mp.end() |
mp.erase(it) |
删除迭代器对应的键和值O(1) |
mp.erase(key) |
根据映射的键删除键和值O(logN) |
mp.size() |
返回映射的对数O(1) |
mp.clear() |
清空map中的所有元素O(N) |
mp.insert() |
插入元素,插入时要构造键值对 |
mp.erase(first,last) |
删除左闭右开区间迭代器对应的键和值O(last−first) |
mp.empty() |
如果map为空,返回true,否则返回false |
mp.begin() |
返回指向map第一个元素的迭代器(地址) |
mp.end() |
返回指向map尾部的迭代器(最后一个元素的下一个地址) |
mp.rbegin() |
返回指向map最后一个元素的迭代器(地址) |
mp.rend() |
返回指向map第一个元素前面(上一个)的逆向迭代器(地址) |
mp.count(key) |
查看元素是否存在,因为map中键是唯一的,所以存在返回1,不存在返回0 |
mp.lower_bound() |
返回一个迭代器,指向键值>= key的第一个元素 |
mp.upper_bound() |
返回一个迭代器,指向键值> key的第一个元素 |
(1)正向遍历
map mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.begin();
while(it != mp.end()) {
cout << it->first << " " << it->second << "\n";
it ++;
}
(2)反向遍历
map mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.rbegin();
while(it != mp.rend()) {
cout << it->first << " " << it->second << "\n";
it ++;
}
(3)二分查找
//二分查找lower_bound() / upper_bound(),map的二分查找以第一个元素(即键为准),对键进行二分查找,返回值为map迭代器类型
#include
using namespace std;
int main() {
map m{{1, 2}, {2, 2}, {1, 2}, {8, 2}, {6, 2}};//有序
map::iterator it1 = m.lower_bound(2);
cout << it1->first << "\n";//it1->first=2
map::iterator it2 = m.upper_bound(2);
cout << it2->first << "\n";//it2->first=6
return 0;
}
(4)添加元素
//方式一
mp["学习"] = "看书";
mp["玩耍"] = "打游戏";
//方式二,插入元素构造键值对
mp.insert(make_pair("vegetable","蔬菜"));
//方式三
mp.insert(pair("fruit","水果"));
//方式四
mp.insert({"hahaha","wawawa"});
(5)访问元素
下标访问:
mp["菜哇菜"] = "强哇强";
cout << mp["菜哇菜"] << "\n";//只是简写的一个例子,程序并不完整
遍历访问:
//方式一:迭代器访问
map::iterator it;
for(it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second << "\n";
}
//方式二:智能指针访问
for(auto i : mp)
cout << i.first << " " << i.second << endl;//键,值
//方式三:对指定单个元素访问
map::iterator it = mp.find('a');
cout << it -> first << " " << it->second << "\n";
set容器中的元素不会重复,当插入集合中已有的元素时,并不会插入进去,而且set容器里的元素自动从小到大排序。
代码 |
含义 |
s.begin() |
返回set容器的第一个元素的地址(迭代器)O(1) |
s.end() |
返回set容器的最后一个元素的下一个地址(迭代器)O(1) |
s.rbegin() |
返回逆序迭代器,指向容器元素最后一个位置O(1) |
s.rend() |
返回逆序迭代器,指向容器第一个元素前面的位置O(1) |
s.clear() |
删除set容器中的所有的元素,返回unsigned int类型O(N) |
s.empty() |
判断set容器是否为空O(1) |
s.insert() |
插入一个元素 |
s.size() |
返回当前set容器中的元素个数O(1) |
erase(iterator) |
删除定位器iterator指向的值 |
erase(first,second) |
删除定位器first和second之间的值 |
erase(key_value) |
删除键值key_value的值 |
s.find(element) |
查找set中的某一元素,有则返回该元素对应的迭代器,无则返回结束迭代器 |
s.count(element) |
查找set中的元素出现的个数,由于set中元素唯一,此函数相当于查询element是否出现 |
s.lower_bound(k) |
返回大于等于k的第一个元素的迭代器O(logN) |
s.upper_bound(k) |
返回大于k的第一个元素的迭代器O(logN) |
(1)访问
//迭代器访问
for(set::iterator it = s.begin(); it != s.end(); it++)
cout << *it << " ";
//智能指针
for(auto i : s)
cout << i << endl;
//访问最后一个元素
//第一种
cout << *s.rbegin() << endl;
//第二种
set::iterator iter = s.end();
iter--;
cout << (*iter) << endl; //打印2;
//第三种
cout << *(--s.end()) << endl;
(2)改变set排序规则
//基础数据类型
//方式一:改变set排序规则,set中默认使用less比较器,即从小到大排序。(常用)
set s1; // 默认从小到大排序
set > s2; // 从大到小排序
//方式二:重载运算符。(很麻烦,不太常用,没必要)
//重载 < 运算符
struct cmp {
bool operator () (const int& u, const int& v) const {
// return + 返回条件
return u > v;
}
};
set s;
for(int i = 1; i <= 10; i++)
s.insert(i);
for(auto i : s)
cout << i << " ";
//方式三:初始化时使用匿名函数定义比较规则
set> s([&](int i, int j){
return i > j; // 从大到小
});
for(int i = 1; i <= 10; i++)
s.insert(i);
for(auto x : s)
cout << x << " ";
//高级数据类型(结构体)
//直接重载结构体运算符即可,让结构体可以比较。
struct Point {
int x, y;
bool operator < (const Point &p) const {
// 按照点的横坐标从小到大排序,如果横坐标相同,纵坐标从小到大
if(x == p.x)
return y < p.y;
return x < p.x;
}
};
set s;
for(int i = 1; i <= 5; i++) {
int x, y;
cin >> x >> y;
s.insert({x, y});
}
for(auto i : s)
cout << i.x << " " << i.y << "\n";
(3)其他set
(1)初始化和赋值操作
//头文件
#include
//1.初始化定义
pair p("wangyaqi",1);//带初始值的
pair p;//不带初始值的
//2.赋值
p = {"wang", 18};
p = make_pair("wang", 18);
p = pair("wang", 18);
(2)访问
//定义结构体数组
pair p[20];
for(int i = 0; i < 20; i++) {
//和结构体类似,first代表第一个元素,second代表第二个元素
cout << p[i].first << " " << p[i].second;
}
代码 |
含义 |
s.size()和s.length() |
返回string对象的字符个数,他们执行效果相同。 |
s.max_size() |
返回string对象最多包含的字符数,超出会抛出length_error异常 |
s.capacity() |
重新分配内存之前,string对象能包含的最大字符数 |
s.push_back() |
在末尾插入 |
s.insert(pos,element) |
在pos位置插入element |
s.append(str) |
在s字符串结尾添加str字符串 |
erase(iterator p) |
删除字符串中p所指的字符 |
erase(iterator first, iterator last) |
删除字符串中迭代器区间[first,last)上所有字符 |
erase(pos, len) |
删除字符串中从索引位置pos开始的len个字符 |
clear() |
删除字符串中所有字符 |
s.replace(pos,n,str) |
把当前字符串从索引pos开始的n个字符替换为str |
s.replace(pos,n,n1,c) |
把当前字符串从索引pos开始的n个字符替换为n1个字符c |
s.replace(it1,it2,str) |
把当前字符串[it1,it2)区间替换为str it1 ,it2为迭代器哦 |
tolower(s[i]) |
转换为小写 |
toupper(s[i]) |
转换为大写 |
s.substr(pos,n) |
截取从pos索引开始的n个字符 |
s.find (str, pos) |
在当前字符串的pos索引位置(默认为0)开始,查找子串str,返回找到的位置索引,-1表示查找不到子串 |
s.find (c, pos) |
在当前字符串的pos索引位置(默认为0)开始,查找字符c,返回找到的位置索引,-1表示查找不到字符 |
s.rfind (str, pos) |
在当前字符串的pos索引位置开始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串 |
s.rfind (c,pos) |
在当前字符串的pos索引位置开始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符 |
s.find_first_of (str, pos) |
在当前字符串的pos索引位置(默认为0)开始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_first_not_of (str,pos) |
在当前字符串的pos索引位置(默认为0)开始,查找第一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_last_of(str, pos) |
在当前字符串的pos索引位置开始,查找最后一个位于子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_last_not_of ( str, pos) |
在当前字符串的pos索引位置开始,查找最后一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串 |
(1)初始化及定义
//头文件
#include
string str1; //生成空字符串
string str2("123456789"); //生成"1234456789"的复制品
string str3("12345", 0, 3);//结果为"123" ,从0位置开始,长度为3
string str4("123456", 5); //结果为"12345" ,长度为5
string str5(5, '2'); //结果为"22222" ,构造5个字符'2'连接而成的字符串
string str6(str2, 2); //结果为"3456789",截取第三个元素(2对应第三位)到最后
(2)排序
sort(s.begin(),s.end()); //按ASCII码排序
初始化方法:
代码 |
含义 |
bitset < n >a |
a有n位,每位都为0 |
bitset < n >a(b) |
a是unsigned long型u的一个副本 |
bitset < n >a(s) |
a是string对象s中含有的位串的副本 |
bitset < n >a(s,pos,n) |
a是s中从位置pos开始的n个位的副本 |
方法函数:
代码 |
含义 |
b.any() |
b中是否存在置为1的二进制位,有 返回true |
b.none() |
b中是否没有1,没有 返回true |
b.count() |
b中为1的个数 |
b.size() |
b中二进制位的个数 |
b.test(pos) |
测试b在pos位置是否为1,是 返回true |
b[pos] |
返回b在pos处的二进制位 |
b.set() |
把b中所有位都置为1 |
b.set(pos) |
把b中pos位置置为1 |
b.reset() |
把b中所有位都置为0 |
b.reset(pos) |
把b中pos位置置为0 |
b.flip() |
把b中所有二进制位取反 |
b.flip(pos) |
把b中pos位置取反 |
b.to_ulong() |
用b中同样的二进制位返回一个unsigned long值 |
(1)bitset进行位操作
bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));
cout << (foo^=bar) << endl;// 1010 (foo对bar按位异或后赋值给foo)
cout << (foo&=bar) << endl;// 0001 (按位与后赋值给foo)
cout << (foo|=bar) << endl;// 1011 (按位或后赋值给foo)
cout << (foo<<=2) << endl;// 0100 (左移2位,低位补0,有自身赋值)
cout << (foo>>=1) << endl;// 0100 (右移1位,高位补0,有自身赋值)
cout << (~bar) << endl;// 1100 (按位取反)
cout << (bar<<1) << endl;// 0110 (左移,不赋值)
cout << (bar>>1) << endl;// 0001 (右移,不赋值)
cout << (foo==bar) << endl;// false (1001==0011为false)
cout << (foo!=bar) << endl;// true (1001!=0011为true)
cout << (foo&bar) << endl;// 0001 (按位与,不赋值)
cout << (foo|bar) << endl;// 1011 (按位或,不赋值)
cout << (foo^bar) << endl;// 1010 (按位异或,不赋值)
(2)访问
//可以通过 [ ] 访问元素(类似数组),注意最低位下标为0,如下:
bitset<4> foo ("1011");
cout << foo[0] << endl; //1
cout << foo[1] << endl; //0
cout << foo[2] << endl; //1
成员函数 |
功能 |
begin() |
返回容器中第一个元素的访问迭代器(地址) |
end() |
返回容器最后一个元素之后一个位置的访问迭代器(地址) |
rbegin() |
返回最后一个元素的访问迭代器(地址) |
rend() |
返回第一个元素之前一个位置的访问迭代器(地址) |
size() |
返回容器中元素的数量,其值等于初始化 array 类的第二个模板参数N |
max_size() |
返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N |
empty() |
判断容器是否为空 |
at(n) |
返回容器中 n 位置处元素的引用,函数会自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常 |
front() |
返回容器中第一个元素的直接引用,函数不适用于空的 array 容器 |
back() |
返回容器中最后一个元素的直接引用,函数不适用于空的 array 容器。 |
data() |
返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能 |
fill(x) |
将x这个值赋值给容器中的每个元素,相当于初始化 |
array1.swap(array2) |
交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型 |
(1)声明和初始化
//基础数据类型
//声明一个大小为100的int型数组,元素的值不确定
array a;
//声明一个大小为100的int型数组,初始值均为0(初始值与默认元素类型等效)
array a{};
//声明一个大小为100的int型数组,初始化部分值,其余全部为0
array a{1, 2, 3};
//或者可以用等号
array a = {1, 2, 3};
//高级数据类型
//不同于数组的是对元素类型不做要求,可以套结构体
array s = {"ha", string("haha")};
array a;
(2)取存元素
//修改元素
array a = {1, 2, 3, 4};
a[0] = 4;
//️访问元素
//下标访问
array a = {1, 2, 3, 4};
for(int i = 0; i < 4; i++)
cout << a[i] << " \n"[i == 3];
//利用auto访问
for(auto i : a)
cout << i << " ";
//️迭代器访问
auto it = a.begin();
for(; it != a.end(); it++)
cout << *it << " ";
//at()函数访问
//下标为1的元素加上下标为2的元素,答案为5
array a = {1, 2, 3, 4};
int res = a.at(1) + a.at(2);
cout << res << "\n";
//get方法访问
//将a数组下标为1位置处的值改为x
//注意:获取的下标只能写数字,不能填变量
get<1>(a) = x;
(3)部分成员函数使用
data();//指向底层元素存储的指针。对于非空容器,返回的指针与首元素地址比较相等。
a.fill(x);//array的fill()函数,将a数组全部元素值变为x
fill(a.begin(), a.end(), x);//将a数组[begin,end)全部值变为x
sort(a.begin(), a.end());//排序
(1)声明和初始化
//声明一个空的tuple三元组
tuple t1;
//赋值
t1 = make_tuple(1, 1, "hahaha");
//创建的同时初始化
tuple t2(1, 2, 3, 4);
//可以使用pair对象构造tuple对象,但tuple对象必须是两个元素
auto p = make_pair("wang", 1);
tuple t3 {p}; //将pair对象赋给tuple对象
(2)元素操作
//获取tuple对象t的第一个元素
int first = get<0>(t);
//修改tuple对象t的第一个元素
get<0>(t) = 1;
(3)函数操作
//获取元素个数
tuple t(1, 2, 3);
cout << tuple_size::value << "\n"; // 3
//获取对应元素的值
//通过get(obj)方法获取,n必须为数字不能是变量
tuple t(1, 2, 3);
cout << get<0>(t) << '\n'; // 1
cout << get<1>(t) << '\n'; // 2
cout << get<2>(t) << '\n'; // 3
//通过tie解包 获取元素值
//tie可以让tuple变量中的三个值依次赋到tie中的三个变量中
int one, three;
string two;
tuple t(1, "hahaha", 3);
tie(one, two, three) = t;
cout << one << two << three << "\n"; // 1hahaha3