浅浅的介绍一下STL
这里主要介绍stl容器中的vector,set,pair,map
·
1.什么是STL
STL
全称
(Standard Template Library),就是标准模板库,说人话就是C++标准里帮我们写好了一些经常用到的东西,其中包括容器(就是存东西的)、算法(例如之前学过的sort
)、迭代器(可以看成功能更强大的指针)。
灵活的运用STL,可以方便我们写代码,减少许多不必要的思维量(毕竟一大票东西别人都写好了,写的还比你自己写的好用)。比如
: sdnu1060
,
sdnu1090。
但是滥用
STL
也有风险:
1. STL里有很多竞赛不需要的附加功能,运行速度肯定会比手写慢,在竞赛中可能会因此得到
TLE
。
2.
容易出现一些奇奇怪怪的隐性BUG,并且还很难找到(所以这些东西手写也得会,语法糖吃多了不好)。
3. 考场上容易忘使用方法,或者混淆使用方法并且自以为正确并且样例看起来也确实正确。
2.容器(Containers)
2.1 vector
vector
:不定长数组
(
动态数组
)
。 我们知道在C语言中,数组大小一旦确定就不能更改,但是有时候我们确实需要动态改变数组的大小,于是
C++
中就提供了一个不定长数组。
头文件:#include<vector>
指定长度或初始值的初始化
int mian(){
vector<int> num1;//定义了一个名为num1,大小为 0,存放int类型数据的数组
vector<double> num2;//定义了一个名为num2,大小为0,存放double类型数据的数组
vector<Node> num3;//还可以存放自己定义的结构体类型
return 0;
}
int mian(){
vector<int> a(n);//定义了长度为n的动态数组a,下标和一般数组一样,从0开始
vector<int> b(n,0);//定义了长度为n的动态数组b,并且初值全为0
vector<int> c(n.100);//定义了长度为n的动态数组c,并且初值全为100
return 0;
}
int mian(){
vector<int> a={1,4,2,7,5,8};//数组大小为6,元素顺序为1,4,2,7,5,8
vector<int> b=a;//与一般数组不同,动态数组可以直接拷贝,而普通的数组不行哦
return 0;
} //元素的初始化
定义二维数组
int mian(){
vector<int> a[5];// 本质等于定义了5个 vector, 分别是a[0], a[1] ... a[4]
// 显然这种方式是混合C的数组的,所以显然这个5就 不能改变了,但是a[0] ~ a[4]
的大小是可以改变的
// 这种写法只有列可以改变大小,行不能改变大小
return 0;
}
这里对于二维数组有个形象生动的比喻,以上面的动态二维数组为例,我们将用vector<int> 定义的5个vector.a[0]--a[4]看成一根葡萄藤上的5个节点,将每个vector.a里的元素个数看成每个节点下结的若干个葡萄,这样是不是就好理解多了,节点是不能改变的,而节点下结的葡萄个数却是可以动态增多的。
所以我们有下面的定义方式
int mian(){
vector<vector<int>> a(5); // 这样就有一个 行列都可变长的二维数组了
vector<vector<int>> a(5,vector<int> (10, 0)); // 初始化列就等于又初始化一个 vector
//定义了一个二维数组,其中初始化有5个葡萄节点,节点数量可以改变,每
个节点下初始化有10个葡萄,葡萄数量可以改变,每个葡萄重量初始化为0。
return 0;
}
这时候大家就会发现,如果要定义多维的数组就要嵌套多个vector,非常麻烦,因此C++ 17之后支持的初始化方式
vector a(5, vector<int>(10));
访问元素:基本和C数组一样,通过下标访问、遍历
vector<int> a(n);//定义一个长度为n的数组
for(int i=0;i<n;i++){
cin>>a[i]; //这里循环输入和普通数组一样
}
int res=a[0]; //访问首个元素
C++还提供了智能指针,能自动按顺序访问所有元素(特别注意,sdnuoj系统非常古老,所以提交智能指针会pe)
vector<int> a(n);//定义一个长度为n的数组
for(int i:a){
cout<<i<<'\n';
}
//如果你甚至懒得想这个数组是什么类型的,还可以 用auto
for(auto i:a){
cout<<i<<'\n';
}
另外STL里的容器也可以使用迭代器访问元素,所谓迭代器可以理解为一种更为强大的指针就行了。(为什么要用迭代器呢?因为STL里面的容器需要实现功能很多,传统指针不够用)
vector<int> a(n);
vector<int> :: iterator i = a.begin();//定义了一个vector<int>类型的迭代器,并且指向了a数组的开头
cout<< *i <<'\n';//输出i所指向的内容;
for(i=a.begin();i!=a.end();i++){
cout<<*i<<'\n';
}
// 同样如果懒得写上面这一坨类名并且还有一堆不明含义 的冒号,也可以使用auto
auto i = a.begin(); // 遍历同理也可以用这种
for(auto i : a){
cout << i << '\n';
}
此外关于vector的函数就太多了,这里浅浅的列举出一些常用的,建议用一个带有代码补全的编辑器,学会看 函数原型以及用户文档来了解。
附上C++的用户手册:https://cplusplus.com/reference/
例题: P1066、P1614
2.2 set(
集合
)
set中的元素不会重复,如果插入了重复的元素,会自动忽略该次插入操作,并且
set
里面的元素自动从小到大排序。
头文件以及定义
#include <set>
set<int> st; // 定义一个名为set,存放int类型数据的集合
set<int> st2 = st; //也可以使用类似vector一样的拷贝赋值
一些基本操作
set<int> st;
st.insert(5);
st.insert(0);st.insert(3);
st.insert(1);
ST.insert(3);
st.insert(5);//由于原先已经存入5了,所以这次存入会被忽略
cout << st.size() << '\n'; // 输出st的大 小,很明显是4
cout << st.count(5) << '\n'; // count(x)查询x出现的次数,但是set里元素只能出 现一次,
// 所以可以 用来判断x是否在x里出现
注意:set与vector不同,不能通过下标来访问,因此就必须使用迭代器或者智能指针
set<int> :: iterator i = st.begin();
cout<< *i << endl;//输出第一个元素
i = next(i); //迭代器后移,访问下一个元素
cout<< *i <<endl; //输出下一个元素
//当然可以通过迭代器遍历输出
for(i=st.begin(); i!=st.end() ; i++){
cout<< *i <<endl;
}
//或者智能指针
for(auto i : st){
cout<< *i <<endl;
}
再来介绍一些关于set的函数
由set衍生来的一些容器:
unordered_set<int> a; // 无序集合,元素无序,但是只能出现一次
multiset<int> b; //多重集合,可以存在多个元素
unordered_multiset<int> c //无序多重集合
例题:sdnu1209、sdnu1058
2.3 pair (二元组)
pair里面含有两个元素,可以看作是只有两个元素的结构体。pair只是一个简单二元组,所以没有特别的操作,不过重载了’<‘
运算符使得首先比较
first元素,嗦人话就是优先按照first
元素来比较大小,
first
元素相同时在按
second元素来比较大小
头文件以及初始化
#include<utility>
pair<int, int> p; // 定义了一个first元素为 int, second元素为int的二元组
p = {5, 3}; // 讲first元素赋值为5, second 元素赋值为3(在一些老版本的编译器上可能用不了)
//如果需要将一个5和3结合成二元组,可以使用函数 make_pair();
p = make_pair(5, 3);
访问元素
pair<int, int> p = {5, 3};
// 要访问第一个元素就使用p.first, 要访问第二个 元素就使用p.second;
int a = p.first, b = p.second;
// pair<int, int> 其实等价于下面的结构体
struct pair{
int first, second;
};
2.4 map(映射)
map
就类似数学上的函数。长这个样子-> map<int, int>mp
。可以看到
map
也有
first
、
second
两个元素,我们把first叫做键值
(key)
,把
second
叫做值
(value)
。在
map里面,一个
key
对应一个
value
,类似函数里一个
x
对应一个
y。并且在map
里,
x不能重复,自动按从小到大排序。
可以把
map
看成由一堆
pair
组成的
set
头文件以及初始化
#include<map>
map<int,int> mp; //定义了一个int映射int的map;
map<int,string> mp; //定义了一个string映射int的map;
map<char,int> mp; //定义了一个char映射int的map;
map<int, node> mp // 定义了一个int映射node的map(node是结构体)
基本操作
map<int,int> mp;
mp.insert(make_pair(1,2)); // 插入key为 1,value为2的映射
mp.insert({1,2}); // 在C++11版本之后,可 以用花括号来生成结构体了
cout<< mp[1] <<endl; //输出1的映射,显然是2
mp[1]=3; //把原先的1映射2,改成1映射3
cout<< mp[1] <<endl; //显然这时候会输出3
cout<< mp[2]<<endl; //如果原先没有定义2的映射,那么自动生成2映射0;
遍历map 以及两种智能指针
//遍历map
map<int, int> :: iterator i;
for(i = mp.begin() ; i!= mp.end(); i++){
cout<< *i.first <<' ' << *i.second;
}
//智能指针
for( auto i: mp){
cout<< i.first <<' ' << i.second;
}
//另一种智能指针
for(auto[x ,y] : mp){
cout<< x << ' ' << y <<'\n' ;
}
这里再介绍一些其他关于map的操作
由
map
衍生来的一些容器
unorder_map<int, int> mp; // 无序
到这里常用的容器就基本结束了,还有一些可能会用到的容
器,感兴趣可以自行了解:
bitset
、
array
、
tuple
等
例题:sdnu1058、sdnu1703
更多推荐
已为社区贡献1条内容
所有评论(0)