•前言:
在算法比赛中,如果每一个数据结构都自己手搓的话,将会花费大量的时间来重复造轮子,因此在这里,我总结了一些在算法比赛里 经常使用到的STL的数据结构。
那么我们一起来看看吧。
一.vector
• 初始化:
cpp
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> v;//普通初始化,后需要用等于赋值
vector<int> v(3); // 指定空间,内容为空,等价于 vector<int> v(3,0)
vector<int> v (3,-1); //指定空间为 3 ,内容为-1
//二维数组
vector<vector<int>> m(4,vector<int>(5,2));
}
这里重点说一下这个 二维数组, 指定二维数组初始化值的时候,在构造函数括号里第二个代表列的位置,写下vector<数据类型>(几列,初始化为几).
• 常规操作:
cpp
//交换容器
v.swap(v1);
//插入
v.insert(v.begin()+3,5);
}
swap()可以互换容器, insert()和erase()中的参数 是 "迭代器",
v.begin()代表的是数组索引为0的位置**,因此v.begin() + i 就是等于计算数组索引。**
•补充: 二维数组多用于 "网格" ,"矩阵"中,特定情况下用于 实现 "前缀和数组" 前缀和数组-----从零开始的算法-CSDN博客
二.string
string容器支持 "++自动扩容" , "拼接" ,"比较" 登操作++
size() 和 length()成员函数实现的效果,不能说完全相同吧,只能说一模一样,用哪个没区别。
cpp
#include<iostream>
#include<string>
using namespace std;
int main(){
string a = "qwq";
//拼接
a = a + "Hello";
//用括号访问元素
cout << a[0] << endl;
//截取字串 substr(起始位置,长度)
cout << a.substr(0,3) << endl;
system("pause");
return 0;
}
拼接操作的话,直接使用 "+",在例子中直接把hello,接在了a的后面。
接口 substr(起始位置,长度) ,用于截取字符串中的某一段元素
• 查找函数find() ,比较
cpp
//find()查找
cout << a.find("Hello") << endl;
//比较
cout << (a == "H") << endl;
cout << (a != "H") << endl;
find()查找到这个元素之后,会返回首先出现位置的下标。
比较: 当符合 运算式时候,会返回 1 ,不符合返回0.
1.字符串和数字的转换
• 字符串转数字
cpp
#include<iostream>
#include<string>
using namespace std;
int main(){
//字符串转int
int n = stoi("567");
cout << n << endl;
//字符串转longlong
long long m = stoll("5678910");
cout << m << endl;
++**stoi()**字符串转 int , stoll() 字符串转longlong++
• 数字转字符串
cpp
//数字转字符串
string s = to_string(520);
cout << s << endl;
string str = to_string(52.0);
cout << str << endl;
++to_string()++ 可以把 数字转为字符串。
2. char类型 单字符的判断和转换
<cctype> 提供了函数用于处理 单字符的判断和转换。
• isdigit() 判断是否为 0 - 9 的 数字字符 ,++是返回非零整数 ,不是返回0++
cpp
//isdigit 是否为数字字符 "0" - "9"
cout << (isdigit('5') != 0) << endl;
cout << (isdigit('a') != 0) << endl;
运行结果是 :1 0
• isalpha() 是否为字符 , 是返回非零整数 ,不是返回 0
cpp
// isalpha()
cout << (isalpha('a') != 0) << endl;
• toupper , tolower 大小写转换
toupper(ch) , tolower(ch) 返回的是 int 类型 ,把ch 转换为 "int" 类型 ,
想得到转换之后的原形怎么办? 很简单! 强制转换为( char )就好了。
ps : 如果把小写字符输入小写转换,会返回本身;大写转换同理
补充一点: isupper() 和 islower() 判断是否是大小写, 是的话返回非0,不是的话返回0
cpp
// cout << (char)islower('a') << endl;
cout << (char)toupper('a') << endl;
cout << (char)tolower('a') << endl;
运行结果 : A a
技巧: 字母转数字索引
算法题中非常常见,这个技巧常用于将字母字符映射为从 0 开始的整数索引,便于在数组或哈希表中进行计数、查找等操作。
cpp
char c = 'd';
int index = c - 'a'; // index = 3
index 为 3
三.list
list 是 双向链表,支持任意位置插入和删除 ,但是不支持像数组那样随机下标访问元素,因此只能靠迭代器遍历.
前情提要:++advance()是移动迭代器。++,顾名思义就是能把迭代器移动的迭代器。
听起来有点绕,请看下文就会理解了
cpp
#include<iostream>
#include<list>
using namespace std;
int main(){
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_front(5);
lt.push_front(4);
for (auto node : lt){
cout << node << " ";
}
cout << endl;
auto i = lt.begin();
advance(i,3);
lt.insert(i,50);
for (auto node : lt){
cout << node << " ";
}
cout << endl;
auto it = lt.begin();
advance(it,2);
lt.erase(it);
for (auto node : lt){
cout << node << " ";
}
system("pause");
}
advance(迭代器,向后移动几个位置)会移动迭代器 ,配合insert()和 erase(),有助于完成插入和删除操作
在这个例子中:
i 一开始是起始迭代器,通过 advance 移动了 3 个位置,然后插入了 50
四.unordered_map 哈希表
语法:
• 初始化
unordered_map<std:: string , ElementType> 变量名
<>中的第一个数据类型是键, 第二个数量类型代表的是值;通常第一个数据类型用string来表示。
• 添加键值 --- 直接用【】
【】中填键,= 号后面填值
cpp
#include<iostream>
#include<unordered_map>
using namespace std;
int main(){
unordered_map<string,int> map;
map["man"] = 3;
map["cherno"] = 5;
map["gg"] = 6;
cout << map["gg"] << endl;
cout << map["tl"] << endl;
system("pause");
return 0;
}
直接通过 形式 :变量名【键】 = value;
访问的时候,如果这个键不存在 ,就比如说在这个例子中
++访问不存在的键 map【tl】 会返回 0(默认)----为什么呢?++
++因此 即使没有给键确定的 value ,它也会默认为 0.++
• 查找函数
map.count("要查找的元素") ---- C++ 11 中 利用 成员函数count 查找,如果存在会返回1 , 不存在返回 0.
• 删除
cpp
cout << map.count("tl") << endl;
cout << map.size() << endl; // 返回 4
map.erase("tl");
cout << map.size() << endl; // 返回 3
system("pause");
return 0;
第一个返回 4 ,第二个 返回 3
• 遍历
cpp
//遍历:
for (const auto& node : map){
cout << node.first << " " << node.second << endl;
}
first 就是键 ,second 就是 对应的值。
应用:
1. unordered_map 在算法题中的用途就是 统计频率
cpp
unordered_map<char,int> test;
string s = "hello";
for (auto &node : s){
test[node]++;
}
//遍历
for (auto & node: test){
cout << node.first << " " << node.second << endl;
}
通过遍历循环,然后存入哈希表中。
五.map --- 有序表--之后更新
六.unordered_set 哈希集合
哈希集合本质上就是底层在哈希表的基础上拓展而来的,
++相较于哈希表,哈希集合是不会储存重复元素的++
哈希集合只储存 key,不储存对应的value ----不储存相同元素的底层原因。
例子: unordered_set<int> a; // 之前已经插入过 10
当 a.insert(10) : 最后哈希表中不会有两个10,循环后会发现只有一个10.
• 初始化:
unorder_set <数据类型> 变量名
•插入 insert()
cpp
#include<iostream>
#include<unordered_set>
using namespace std;
int main(){
unordered_set<int> a;
a.insert(1);
a.insert(5);
a.insert(10);
for (auto &node : a){
cout << node << " ";
}
cout << endl;
//重复插入无效
a.insert(10);
for (auto &node : a){
cout << node << " ";
}
cout << endl;
system("pause");
return 0;
}
通过两次遍历会发现,a中只储存了一个 10
遍历同理---哈希表底层是数组。
七. set 有序集合---后续更新
八.队列 / 栈
详细请看这一篇:队列/栈的基本原理与实现----从零开始的数据结构学习-CSDN博客
九. priority_queue---优先队列---后续更新
十.pair ---结构化绑定
pair的头文件有点不同 ,不是以本身命名的-----#include ++<utility>++
pair 实际上就是类似结构体的逻辑,能够把不同的数据类型整合成一个类型
• 初始化
pair <第一个数据类型,第二数据类型> 变量名
成员变量中: first---第一个数据, second --- 第二个数据(这里和哈希表类似)
cpp
#include<iostream>
#include<utility>
using namespace std;
int main(){
pair<string,int> p1("man",3);
pair<string,int> p2 = {"ok",5};
• 比较
直接用比较运算符就行
第一个对象如果相同,就会比较第二对象 ,
正确就输出1,错误就输出0
cpp
pair<string,int> p1("man",3);
pair<string,int> p2 = {"ok",5};
cout << p1.first << endl;
cout << p1.second << endl;
cout << (p1 > p2) << endl;
会输出0,第一个类型中 ok 的 "o" 大于 man 中的 "m" (好吧我承认这个变量有点奇怪)。
结语:
如果喜欢的话,不妨点一个免费的赞和收藏,你们的支持就是我不断前进的动力之一。
我后续也会不断更新优秀的文章,欢迎各位股东关注!!!
当然,这篇文章还有没有更新完的部分,我后续会不断改进的, respect 。