C++的STL库
一、vector动态数组
1、概述
动态数组,也就是不定长数组,数组的长度是可以根据我们的需要动态改变的。
在C++里面有已经写好的标准模板库 STL(Standard Template Library)库,实现了集合、映射表、栈、队列等数据结构和排序、查找等算法。我们可以很方便地调用标准库来减少我们的代码量。
C++ 中动态数组写作vector
,而C语言中没有标准库,这也是为什么参加比赛推荐用 C++ 而不用C的原因。
尽量不要在比赛中使用指针!
2、构造
使用vector
需要引入<vector>
头文件、std
命名空间
泛型T
是我们数组要储存的数据类型,可以是 int
、float
、double
、或者结构体等。初始的时候vector
是空的。
#include "vector"
using namespace std;
int main() {
vector<int> vec;
return 0;
}
3、使用
1)插入元素
push_back(T t)
:在尾部插入一个元素
int main() {
vector<int> vec;
vec.push_back(1); // 1
vec.push_back(2); // 1 2
vec.push_back(3); // 1 2 3
vec.push_back(4); // 1 2 3 4
return 0;
};
2)获取长度与访问元素
size()
:获取长度
vec[index]
:访问元素(类似数组,首位下标是0)
int main() {
vector<int> vec;
vec.push_back(1); // 1
vec.push_back(2); // 1 2
cout << vec.size() << endl; // 2
cout << vec[1] << endl; // 2
return 0;
};
3)修改元素
直接赋值:vec[1] = 1
int main() {
vector<int> vec;
vec.push_back(1); // 1
cout << vec[0] << endl; // 1
vec[0] = 6;
cout << vec[0] << endl; // 6
return 0;
};
4)删除元素
pop_back()
:删除最后一个元素
int main() {
vector<int> vec;
vec.push_back(1); // 1
vec.push_back(2); // 1 2
vec.pop_back(); // 1
return 0;
};
5)清空
调用clear()
方法就可以清空vector
,但不释放内存
vector
清空并清除内存的方法
vector<int>().swap(v);
4、构造函数
我们在定义一个对象的时候可以给他赋予初始值。
第一个参数表示初始的动态数组的长度,第二个参数表示初始的数组里面每个元素的值。(如果不传入第二个参数,那么初始的值都是0。)
vector<int> vec(5,1); // 1 1 1 1 1
5、二维vector
二维动态数组:vector<vector<int> > vec2
<int> >
中间有一个空格,这个空格一定要加上,否则在一些老版本的编译器上将不能通过编译。
因为每一维都是空的,我们必须要一维一维的赋值
vector
的维度可以像数组一样更多,但是超过两维以后操作起来会比较麻烦,所以一般用 vector
都只到两维。
vector<vector<int> > vec2;
for (int i = 0; i < 5; ++i) {
vector<int> temp(i + 1, 1);
vec2.push_back(temp);
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < vec2[i].size(); j++) {
cout << vec2[i][j];
}
cout << endl;
}
return 0;
二、set集合
1、概述
集合是由一些不重复的数据组成的。
2、构造
使用set
需要引入<set>
头文件、std
命名空间
泛型T
是我们数组要储存的数据类型,可以是 int
、float
、double
、或者结构体等。初始的时候map
是空的。
#include "set"
using namespace std;
int main() {
set<int> s;
return 0;
}
3、使用
1)插入元素
insert()
方法向集合中插入一个新的元素。
如果集合中已经存在了某个元素,再次插入不会产生任何效果,集合中是不会出现重复元素的。
set<int> s;
s.insert(1); // 1
s.insert(2); // 1 2
s.insert(1); // 1 2
s.insert(3); // 1 2 3
2)删除元素
erase()
方法删除集合中的一个元素
如果集合中不存在这个元素,不进行任何操作。
set<int> s;
s.insert(1); // 1
s.insert(2); // 1 2
s.erase(1); // 2
s.erase(3); // 2
3)判断元素存在
count()
方法判断元素在set
中出现的次数。
如果集合中存在我们要查找的元素,返回 1,否则会返回 0。
s.count(3);
4)"增强for"遍历
在C++ 中遍历 set 是从小到大遍历的,也就是说 set 会帮我们排序的。
int main() {
set<int> s;
s.insert(1); // 1
s.insert(2); // 1 2
s.insert(1); // 1 2
s.insert(3); // 1 2 3
for (auto i: s) {
cout << i << endl;
}
return 0;
}
5)迭代器遍历
C++通过迭代器可以访问集合中的每个元素,迭代器就好像指针指向set中的某个元素。通过操作这个指针,我们可以改变它指向的元素。
*
(解引用运算符)操作可以获取迭代器指向的元素。++
操作让迭代器指向下个元素,--
操作让迭代器指向上一个元素。
迭代器的写法比较固定,set<T>::iterator it
就定义了一个指向set<T>
这种集合的迭代器it
,T
是任意的数据类型,::iterator
是固定的写法。
begin()
返回容器中起始元素的迭代器,end()
返回容器的尾后迭代器
int main() {
set<int> s;
s.insert(1); // 1
s.insert(2); // 1 2
s.insert(1); // 1 2
s.insert(3); // 1 2 3
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
cout << *it << endl;
}
return 0;
}
6)反向遍历
rbegin()
、rend()
int main() {
set<int> s;
s.insert(1); // 1
s.insert(2); // 1 2
s.insert(1); // 1 2
s.insert(3); // 1 2 3
for (set<int>::iterator it = s.rbegin(); it != s.rend(); it++) {
cout << *it << endl;
}
return 0;
}
7)清空
clear()
方法就可清空 set
,同时会清空 set
占用的内存。
set<int> s;
s.clear();
4、set和结构体
set
经常会配合结构体来使用,用set
来储存结构体和 vector
有些区别。正如我们前面所说的那样,set
是需要经过排序的。
系统自带的数据类型有默认的比较大小的规则,而我们自定义的结构体,系统是不可能知道这个结构体比较大小的方式的。
所以我们需要用一种方式来告诉系统怎么比较这个结构体的大小。其中一种方法叫做运算符重载,我们需要重新定义小于符号。
struct Node {
int x, y;
bool operator<(const Node &rhs) const {
if (x == rhs.x) {
return y < rhs.y;
} else {
return x < rhs.x;
}
}
};
不要漏掉最后的
const
。const
函数表示不能对其数据成员进行修改操作,并且const
对象不能调用非const
成员函数,只允许调用const
成员函数。
三、map映射
1、概述
关键字集合 (key)=> 值集合(value)
2、构造
使用map
需要引入<map>
头文件、std
命名空间。
构造语句:map<T1,T2> m;
#include "map"
using namespace std;
int main() {
map<string,int> mp;
return 0;
}
3、pair
insert()
方法向集合中插入一个新的映射,參数是一个 pair
pair
是一个标准库类型,定义在头文件utility
中。可以看成是有两个成员变量 first
和second
的结构体,并且重载了<
运算符(先比较 first
大小,如果一样再比较second
)。当我们创建一个pair
时,必须提供两个类型。
pair<string, int> p;
make_pair(v1, v2)
函数:返回由v1
和v2
初始化的pair
,类型可以从v1
和v2
的类型推断出来。我们向映射中加入新映射对的时候就是通过插入 pair
来实现的。若key
已存在,本次插入无效。
4、使用
1)pair插入
若key
已存在,本次插入无效。
int main() {
map<string,int> mp;
mp.insert(make_pair("wc",1));
mp.insert(make_pair("wmh",1));
mp.insert(make_pair("wmh",2)); // 无效插入
// {"wmh" -> 1, "wc" -> 1}
return 0;
}
2)下标插入与修改
若key
已存在,下标法能修改值(与make_pair不同)
int main() {
map<string,int> mp;
mp["wmh"] = 1;
mp["wc"] = 2;
mp["wc"] = 3;
// {"wmh" -> 1, "wc" -> 3}
return 0;
}
3)判断映射存在
count(T t)
方法判断元素在map
中出现的次数。
如果集合中存在我们要查找的元素,返回 1,否则会返回 0。
s.count(3);
4)访问映射
直接利用下标访问:mp["rexhao"]
而这里有一个比较神奇的地方:如果没有对"rexhao"做过映射的话,此时你访问mp["rexhao"]
,系统将会自动为"rexhao" 生成一个映射,其value为对应类型的默认值(比如int的默认值是 0, string 的默认值是空字符串)
访问映射前需要判断是否存在
int main() {
map<string, int> mp;
mp.insert(make_pair("wmh", 1));
if (mp.count("wmh")) {
cout << "wmh is in class " << mp["wmh"] << endl;
} else {
cout << "cannot find wmh" << endl;
}
if (mp.count("wc")) {
cout << "wc is in class " << mp["wc"] << endl;
} else {
cout << "cannot find wc" << endl;
}
return 0;
}
5)迭代器遍历
遍历map
是按照关键字(key)从小到大遍历的,这一点和set
有些共性
for (map<string, int>::iterator it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " is in class " << it->second << endl;
}
也可以使用auto
类型
for (auto it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " is in class " << it->second << endl;
}
6)"增强for"遍历
for(auto it : mp){
cout << it.first <<" "<< it.second <<endl;
}
5、二维map
1)map套用set
map套用set:map<int, set<string> > s;
- 插入:
s[2].insert("rexhao");
- 查询:
s[2].count("rexhao");
- 删除:
s[2].erase("rexhao");
2)map套用map
map套用map:map<int,map<string, int> > s;
- rexhao每来2班一次:
s[2]["rexhao"]++;
- 查询rexhao来过2班的次数:
cout << s[2]["rexhao"] << endl;
二维的迭代器遍历
/**
* 二维 map 遍历
* @author 王铭颢
* @Date 2022/11/23 13:29
*/
#include "iostream"
#include "map"
using namespace std;
int main() {
/**
* int1:班级
* string:姓名
* int2:xxx来xx班的次数
*/
map<int, map<string, int> > mp;
// 数据初始化
mp[2]["wmh"] = 5;
mp[3]["wmh"] = 3;
mp[4]["wmh"] = 5;
mp[2]["wcc"] = 1;
for (map<int, map<string, int> >::iterator it = mp.begin(); it != mp.end(); it++) {
for (map<string, int>::iterator itt = it->second.begin(); itt != it->second.end(); itt++) {
cout << it->first << ":" << itt->first << ":" << itt->second << endl;
}
}
// 也可以使用auto类型
return 0;
}