备忘录
数学,杂项与基础
cout<<setprecision(a)<<fixed; //保留小数点后 \(a\) 位
fmod(a,b) //\(a\) 除以 \(b\) 的余数
log2(n) //以 \(2\) 为底的对数运算。结果 \(m\) 与 \(n\) 的关系:\(2^m=n\)
组合数学
\(n\) 选 \(m\) 个人的全排列(即排列数公式):
\(n\) 选 \(m\) 个人的选法(即组合数公式):
- 当 \(m=1\) 时,\(C^m_n=n\);
- 当 \(m=2\) 时,\(C^m_n=\dfrac{n(n-1)}{2}\);
\(n\) 个元素的全排列:
求 \(n\) 个数有多少种出栈方式:
\(n\) 个人围成圆圈的排列方式(圆排列):
对于方程:$ x_1 + x_2 + \cdots + x_k = n$
的非负整数解的个数为:
<< 与 >> 运算符
| 代码 | 作用 |
|---|---|
n << m |
\(n\) 左移 \(m\) 位,即 \(n\) 乘 \(2\) 的 \(m\) 次方 |
n >> m |
\(n\) 右移 \(m\) 位,即 \(n\) 除以 \(2\) 的 \(m\) 次方 |
常量定义
| 代码 | 作用 |
|---|---|
#define man 114514 |
用 \(man\) 当作 \(114514\) 来使用,也可以把任何东西当任何东西用 |
const int n=1en+m |
定义整型常量的值为 \(10^n + m\); |
实用语法,技巧与卡常
operator 重载运算符
当要给 node 类型的 priority_queue 进行升序排序,给出如下代码:
struct node{
int a,b;
}a[100];
priority_queue<node> q;
bool operator<(node x,node y){
return x.a>y.a;
}
这样,就能进行降序排序。其中 operator< 中的 < 不可改变,如果 return x.a>y.a 中的 > 变为 <,那么就变为了降序排序。
读入优化
1.解绑 cin
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}
2.返回值快读
#include<bits/stdc++.h>
using namespace std;
int n,a,ans;
inline int read(){
int x = 0,f = 1;
char c = getchar();
while(!isdigit(c)){
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c)){
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
}
int main(){
n = read();
while(n --) a = read(),ans += a;
printf("%d\n",ans);
return 0;
}
3.传址快读
#include<bits/stdc++.h>
using namespace std;
int n,a,ans;
inline void read(int &x){
x = 0;
int f = 1;
char c = getchar();
while(!isdigit(c)){
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c)){
x = x * 10 + c - '0';
c = getchar();
}
x *= f;
}
int main(){
read(n);
while(n --) read(a),ans += a;
printf("%d\n",ans);
return 0;
}
4.getchar 快速读入
#include<bits/stdc++.h>
using namespace std;
int n,a,ans;
inline void read(int &x){
x = 0;
int f = 1;
char c = getchar();
while(!isdigit(c)){
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c)){
x = x * 10 + c - '0';
c = getchar();
}
x *= f;
}
int main(){
read(n);
while(n --) read(a),ans += a;
printf("%d\n",ans);
return 0;
}
5.getchar_unlocked 快速读入
注:此读入方式在部分环境下不可用,在 CCF 评测环境下可用。
#include<bits/stdc++.h>
using namespace std;
int n,a,ans;
inline void read(int &x){
x = 0;
int f = 1;
char c = getchar_unlocked();
while(!isdigit(c)){
if(c == '-') f = -1;
c = getchar_unlocked();
}
while(isdigit(c)){
x = x * 10 + c - '0';
c = getchar_unlocked();
}
x *= f;
}
int main(){
read(n);
while(n --) read(a),ans += a;
printf("%d\n",ans);
return 0;
}
STL
lower_bound 与 upper_bound
lower_bound(a,b,c) //在内存地址 \((a,b)\) 范围内查找第一个大于等于 \(c\) 的元素,如果找得到就返回这个元素的内存地址,如果找不到返回值为 \(b\)
upper_bound(a,b,c) //在内存地址 \((a,b)\) 范围内查找第一个大于 \(c\) 的元素,如果找得到就返回这个元素的内存地址,如果找不到返回值为 \(b\)
注意: lower_bound(a,a+10,2) 的返回值为 &a[n]。须知,数组 \(a\) 的名字等同于 \(a[0]\) 的内存地址,即 &a[0]。所以 a+10 即 \(a[9]\) 后的那块空间的内存地址。因此 l = &a[3] - a = &a[3] - &a[0] 等于这个元素在数组 \(a\) 中的下标
queue 队列
| 代码 | 作用 |
|---|---|
<queue> |
\(queue\) 队列头文件 |
queue<int> q |
定义初始化:定义一个 \(int\) 类型的队列 \(q\) |
q.front() |
返回队首元素。但不删除该元素 |
q.back() |
返回队尾元素。但不删除该元素 |
q.push() |
在队尾压入新元素 |
q.pop() |
删除队首元素。但不返回其值 |
q.size() |
返回队列中元素个数。返回值类型 unsigned int |
q.empty() |
如列为空返回 \(ture\),否则返回 \(false\) |
deque 双端队列
首尾都可插入和删除的队列为双端队列。
| 代码 | 作用 |
|---|---|
#include<deque> |
\(deque\) 双端队列头文件 |
deque<int> dq |
初始化定义 |
- 函数
| 代码 | 作用 |
|---|---|
push_back(x)/push_front(x) |
把 \(x\) 插入队尾后 |
back()/front() |
返回队尾 / 队首元素 |
pop_back() / pop_front() |
删除队尾 / 队首元素 |
erase(iterator it) |
删除双端队列中的某一个元素 |
erase(iterator first,iterator last) |
删除双端队列中 [first,last) 中的元素 |
empty() |
判断 \(deque\) 是否空 |
size() |
返回 \(deque\) 的元素数量 |
clear() |
清空 \(deque\) |
- 排序
//从小到大
sort(q.begin(), q.end())
//从大到小排序
sort(q.begin(), q.end(), greater<int>());//deque里面的类型需要是int型
sort(q.begin(), q.end(), greater());//高版本C++才可以用
priority_queue 优先队列
优先队列是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。
可以实现每次从优先队列中取出的元素都是队列中优先级最大的一个。
它的底层是通过堆来实现的。
| 代码 | 作用 |
|---|---|
#include<queue> |
头文件 |
priority_queue<int> q |
初始化定义(大根堆) |
priority_queue<int, vector<int>, greater<int>> q |
初始化定义(小根堆) |
- 函数
| 代码 | 作用 |
|---|---|
q.top() |
访问队首元素 |
q.push() |
入队 |
q.pop() |
堆顶(队首)元素出队 |
q.size() |
队列元素个数 |
q.empty() |
是否为空 |
注意没有 clear()
优先队列只能通过 top() 访问队首元素
string 字符串
- 初始化与定义
| 代码 | 作用 |
|---|---|
<string> |
\(string\) 字符串头文件 |
string str |
定义一个空字符串 |
string str2("123456789") |
定义一个内容为 1234456789 的字符串 |
string str("12345", 0, 3) |
结果为 123,从 \(0\) 位置开始,长度为 \(3\) |
string str("123456", 5) |
结果为 12345,长度为 \(5\) |
string str(5, '2') |
结果为 22222,构造 \(5\) 个字符 2 连接而成的字符串 |
string str(str2, 2) |
截取第 \(3\) 个元素(\(2\) 对应第三位)到最后 |
- string 特性
支持比较运算符
\(string\) 字符串支持常见的比较操作符 (>,>=,<,<=,==,!=),支持 string 与 C-string 的比较(如 str < "hello")。
在使用 >,>=,<,<= 这些操作符的时候是根据“当前字符特性”将字符按 字典顺序 进行逐一得 比较。字典排序靠前的字符小, 比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小(前面减后面)。
同时,string ("aaaa") <string(aaaaa)。
支持 + 运算符,代表拼接字符串
\(string\) 字符串可以拼接,通过 + 运算符进行拼接。
string s3=s1+s2 //拼接两个字符串
- 读入
读入字符串,遇空格,回车结束
string s;
cin >> s;
读入一行字符串(包括空格),遇回车结束
string s;
getline(cin, s);
注意: getline(cin, s) 会获取前一个输入的换行符,需要在前面添加读取换行符的语句。如:getchar() 或 cin.get()
错误读取:
int n;
string s;
cin >> n;
getline(cin, s); //此时读取相当于读取了前一个回车字符
正确读取:
int n;
string s;
cin >> n;
getchar(); //cin.get()
getline(cin, s);//可正确读入下一行的输入
- 函数
1.获取字符串的长度
| 代码 | 作用 |
|---|---|
str.size() 和 str.length() |
返回 string 对象的字符个数,他们执行效果相同 |
strlen(str) |
返回 char 类型字符串 \(str\) 的字符个数 |
s.max_size() |
返回 string 对象最多包含的字符数,超出会抛出 length_error 异常 |
s.capacity() |
重新分配内存之前,string 对象能包含的最大字符数 |
sizeof(str) |
获取操作数 \(str\) 所占空间的字节数大小 |
2.插入
| 代码 | 作用 |
|---|---|
s.push_back() |
在末尾插入 |
s.insert(pos,element) |
在 \(pos\) 位置插入\(element\) |
s.append(str) |
在 \(s\) 字符串结尾添加 \(str\) 字符串 |
3.删除
| 代码 | 作用 |
|---|---|
erase(iterator p) |
删除字符串中 \(p\) 所指的字符 |
erase(iterator first, iterator last) |
删除字符串中迭代器区间 [first,last)上所有字符 |
erase(pos, len) |
删除字符串中从索引位置 \(pos\) 开始的 \(len\) 个字符 |
clear() |
删除字符串中所有字符 |
4.字符替换
| 代码 | 作用 |
|---|---|
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\) |
5.大小写转换
方法一
| 代码 | 作用 |
|---|---|
tolower(s[i]) |
转换为小写 |
toupper(s[i]) |
转换为大写 |
方法二
通过 stl 的 transform 算法配合 tolower 和toupper 实现。
有 \(4\) 个参数,前 \(2\) 个指定要转换的容器的起止范围,第 \(3\) 个参数是结果存放容器的起始位置,第 \(4\) 个参数是一元运算。
string s;
transform(s.begin(),s.end(),s.begin(),::tolower);//转换小写
transform(s.begin(),s.end(),s.begin(),::toupper);//转换大写
6.类型转换
| 代码 | 作用 |
|---|---|
stoi(s) |
将字符串 \(s\) 转化为 int 类型 |
stoll(s) |
将字符串 \(s\) 转化为 long long int 类型 |
stod(s) |
将字符串 \(s\) 转化为 double 类型 |
stold(s) |
/将字符串 \(s\) 转化为 long double 类型 |
7.分割
| 代码 | 作用 |
|---|---|
s.substr(pos,n) |
截取从 \(pos\) 索引开始的 \(n\) 个字符 |
8.查找
| 代码 | 作用 |
|---|---|
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_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\) 表示查找不到子串 |
9.其他
| 代码 | 作用 |
|---|---|
str=to_string(a) |
将数组 \(a\) 的元素转到字符串 \(str\) |
sort(s.begin(),s.end()) |
按字符的ASCII码排序 |
reverse(s.begin(), s.end()); |
字符串反转 |
map 映射
map<int/*key*/,string/*volue*/> mymap // \(map\) 的定义:定义一个 \(int\) 类型的键(\(key\))和 \(string\) 类型的值(\(volue\))的 std::map 类型的哈希结构。<int,string> 为 \(key\),\(value\) 键值对,\(key\) 是唯一的,\(value\) 不唯一
mymap.insert(pair<int,string>(1,"s_1")) //insert插入:在键为 1 的地方插入值 s_1
mymap[1]="s_1" //下标使用:在下标(键)为 1 的地方插入值 s_1
mymap.insert ({{1,"s_1"},{2,"s_2"}}) //插入多个:在键为 1 的地方插入值 s_1 并且在键为 2 的地方插入值 s_2
注:\(insert\) 不能插入已有的,但是下标法可以
注:当 \(value\) 为 \(int\) 类型时,可以写 mymyap[]++
unordered_map<int,string>mymap2; // \(unordered\)_\(map\) 的定义:定义一个 \(int\) 类型的键和 \(string\) 类型的值的 std::unordered_map 类型的哈希结构。\(key\) 是唯一的,\(value\) 不唯一
注:std::map 类型的键是会自动排序成有序的,数字按升序,字符按字典序。std::unordered_map 类型则不会对键进行自动排序,是无序的
注:删除或输出一个不存在的数不会报错
- 遍历
方法一:迭代器
auto iter=mymap.begin();
while(iter!=mymap.end()){
cout<<iter->first<<' '<<iter->second<<endl;
iter++;
}
方法二:范围
for(auto & it:mymap){
cout<<it.first<<' '<<it.second<<endl;
}
- 查找
方法一:count函数
函数返回 \(0\) 和 \(1\):如果该 \(map\) 的键存在则输出 \(1\),没有则输出 \(0\)
cout<<mymap.count(5)<<endl<<mymap.count(8)<<endl;
方法二:find()函数
返回一个迭代器,没有的时候返回mymap.end()函数返回的迭代器
auto iter1=mymap.find(3);
if(iter1!=mymap.end())cout<<"cunzai"<<mymap[3]<<endl;
else{
cout<<"meiyou"<<endl;
mymap[3]="s_3";
}
- 删除
方法一:删除某一个,erase()函数
auto iter2=mymap.find(5);
if(iter2!=mymap.end())mymap.erase(iter2);
//或者是直接写mymap.erase(5);//为什么可以传不同的参数,是因为c++的重载性。
方法二:删除全部,clear()函数
mymap.clear();
- 其他
empty() 与 size()
- 对 \(value\) 进行排序
vector<pair<string,int>> vec(mymap1.begin(),mymap1.end());//转移到 vec中
sort(vec.begin(),vec.end(),cmp);
vector 动态数组
- 初始化
1.一维初始化
| 代码 | 作用 |
|---|---|
vector<int> a |
定义了一个名为 \(a\) 的一维数组,数组存储 int 类型数据 |
vector<double> b |
定义了一个名为 \(b\) 的一维数组,数组存储 double 类型数据 |
vector<node> c |
定义了一个名为 \(c\) 的一维数组,数组存储结构体类型数据,node 是结构体类型 |
指定长度和初始值的初始化
| 代码 | 作用 |
|---|---|
vector<int> v(n) |
定义一个长度为 \(n\) 的数组,初始值默认为 \(0\),下标范围 \([0, n - 1]\) |
vector<int> v(n, 1) |
\(v[0]\) 到 \(v[n - 1]\) 所有的元素初始值均为 \(1\) |
注意:指定数组长度之后,指定长度后的数组就相当于正常的数组了
初始化中有多个元素
vector<int> a{1, 2, 3, 4, 5};
//数组a中有五个元素,数组长度就为5
拷贝初始化
vector<int> a(n + 1, 0);
vector<int> b(a); // 两个数组中的类型必须相同,a和b都是长度为n+1,初始值都为0的数组
vector<int> c = a; // 也是拷贝初始化,c和a是完全一样的数组
2.二维初始化
vector<int> v[5] //定义第一维固定长度为 5,第二维可变化的二维数组
注意:行不可变(只有 \(5\) 行), 而列可变,可以在指定行添加元素
vector<int> v[5]可以这样理解:长度为 \(5\) 的 \(v\) 数组,数组中存储的是vector<int>数据类型,而该类型就是数组形式,故 \(v\) 为二维数组。其中每个数组元素均为空,因为没有指定长度,所以第二维可变长。可以进行下述操作:v[1].push_back(2); v[2].push_back(3);
- 函数
| 代码 | 作用 |
|---|---|
v.front() |
返回容器中的第一个数据 |
v.back() |
返回容器中的最后一个数据 |
v.at(idx) |
返回 v[idx] ,会进行边界检查,如果越界会报错,比直接使用 [] 更好一些,常在项目中使用 |
v.size() |
返回实际数据个数(unsigned 类型) |
v.begin() |
返回首元素的迭代器(通俗来说就是地址) |
v.end() |
返回最后一个元素后一个位置的迭代器(地址) |
v.empty() |
判断是否为空,为空返回真,反之返回假 |
v.reserve(sz) |
为数组提前分配 \(sz\) 的内存大小,主要是为了防止在 push_back 过程中多次的内存拷贝 |
v.assign(beg, end) |
将另外一个容器 [x.begin(), x.end()) 里的内容拷贝到 \(v\) 中 |
v.assign(n, val) |
将 \(n\) 个 \(val\) 值拷贝到 \(v\) 数组中,这会清除掉容器中以前的内容,\(v\) 数组的 size 将变为 \(n\) |
v.pop_back() |
删除最后一个数据 |
v.push_back(element) |
在尾部加一个数据 |
v.emplace_back(ele) |
在数组中加入一个数据,和 push_back 功能基本一样,在某些情况下比它效率更高,支持传入多个构造参数 |
v.clear() |
清除容器中的所有元素 |
v.resize(n, v) |
改变数组大小为 \(n\),\(n\) 个空间数值赋为 \(v\),如果没有默认赋值为 \(0\) |
v.insert(pos, x) |
向任意迭代器 \(pos\) 插入一个元素 \(x\) |
v.erase(first, last) |
删除 [first, last) 的所有元素 |
注意情况
-
end()返回的是最后一个元素的后一个位置的地址,不是最后一个元素的地址,所有STL容器均是如此 -
使用
v.resize(n, v)函数时,若 \(v\) 之前指定过大小为 \(pre\)pre > n:即数组大小变小了,数组会保存前 \(n\) 个元素,前 \(n\) 个元素值为原来的值,不是都为 \(v\)pre < n:即数组大小变大了,数组会在后面插入 \(n - pre\) 个值为 v 的元素
也就是说,这个初始值 \(v\) 只对新插入的元素生效。
- 元素访问
1.下标访问
直接和普通数组一样进行访问即可。
//添加元素
for(int i = 0; i < 5; i++)
vi.push_back(i);
//下标访问
for(int i = 0; i < 5; i++)
cout << vi[i] << " ";
cout << "\n";
2.迭代器访问
类似指针,迭代器就是充当指针的作用。
vector<int> vi{1, 2, 3, 4, 5};
//迭代器访问
vector<int>::iterator it;
// 相当于声明了一个迭代器类型的变量it
// 通俗来说就是声明了一个指针变量
- 方式一
vector<int>::iterator it = vi.begin();
for(int i = 0; i < 5; i++)
cout << *(it + i) << " ";
cout << "\n";
- 方式二
vector<int>::iterator it;
for(it = vi.begin(); it != vi.end();it ++)
cout << *it << " ";
//vi.end()指向尾元素地址的下一个地址
// 或者
auto it = vi.begin();
while (it != vi.end()) {
cout << *it << "\n";
it++;
}
vector注意:
vi[i]和*(vi.begin() + i)等价,与指针类似。vector和string的STL容器支持*(it + i)的元素访问,其它容器可能也可以支持这种方式访问,但用的不多,可自行尝试。
stack 栈
- 初始化
| 代码 | 作用 |
|---|---|
#include<stack> |
\(stack\) 栈头文件 |
stack<int> s; |
初始化一个 int 类型的栈 |
- 函数
| 代码 | 作用 |
|---|---|
s.push(ele) |
元素 \(ele\) 入栈 |
s.pop() |
移除栈顶元素 |
s.top() |
取得栈顶元素(但不删除) |
s.empty() |
检测栈内是否为空,空为真 |
s.size() |
返回栈内元素的个数 |
- 栈遍历
栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中
stack<int> st;
for (int i = 0; i < 10; ++i) st.push(i);
while (!st.empty()) {
int tp = st.top(); // 栈顶元素
st.pop();
}
set 集合
set 容器中的元素不会重复,当插入集合中已有的元素时,并不会插入进去,而且 set 容器里的元素自动从小到大排序。
即:set 里面的元素不重复 且有序
| 代码 | 作用 |
|---|---|
#include<set> |
\(set\) 集合头文件 |
set<int> s |
初始化定义(升序) |
set<int, greater<int> > s2 |
初始化定义(降序) |
- 函数
| 代码 | 作用 |
|---|---|
s.begin() |
返回 \(set\) 容器的第一个元素的地址 |
s.end() |
返回 \(set\) 容器的最后一个元素的下一个地址 |
s.rbegin() |
返回逆序迭代器,指向容器元素最后一个位置 |
s.rend() |
返回逆序迭代器,指向容器第一个元素前面的位置 |
s.clear() |
删除 \(set\) 容器中的所有的元素,无返回值 |
s.empty() |
判断 \(set\) 容器是否为空 |
s.insert(element) |
插入一个元素 |
s.size() |
返回当前 \(set\) 容器中的元素个数 |
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\) 的第一个元素的迭代器 |
s.upper_bound(k) |
返回大于 \(k\) 的第一个元素的迭代器 |
- multiset
multiset:元素可以重复,且元素有序
- 注意点一:方法函数基本和
set一样,参考set即可。 - 注意点二:进行删除操作时,要明确删除目标。
删除多个元素:由于元素可以重复,注意使用s.erase(val)方法时,会删除掉所有与 val 相等的元素
删除一个元素:需要删除一个元素时,需要使用s.erase(s.find(val))操作,先找到一个与 \(val\) 相等的元素迭代器,专门删除这个元素
unordered_set:元素无序且只能出现一次
unordered_multiset:元素无序可以出现多次
bitset 位集合
bitset 在 bitset 头文件中,它类似数组,并且每一个元素只能是 \(0\) 或 \(1\),每个元素只用 \(1\) \(bit\) 空间
- 初始化定义
| 代码 | 作用 |
|---|---|
#include<bitset> |
\(pair\) 二元组头文件 |
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 值 |
pair 二元组
pair 只含有两个元素,可以看作是只有两个元素的结构体。
应用:
- 代替二元结构体
- 作为map键值对进行插入
map<string, int> mp;
mp.insert(pair<string, int>("xingmaqi",1));
// mp.insert(make_pair("xingmaqi", 1));
// mp.insert({"xingmaqi", 1});
- 初始化操作和赋值操作
| 代码 | 作用 |
|---|---|
#include<utility> |
头文件 |
pair<string, int> p |
初始化定义(不带初始值) |
pair<string, int> p("wangyaqi",1) |
初始化定义(带初始值) |
| p = | 赋值 |
| p = make_pair("wang", 18) | 赋值 |
| p = pair<string, int>("wang", 18) | 赋值 |
- 访问
//定义结构体数组
pair<int,int> p[20];
for(int i = 0; i < 20; i++) {
//和结构体类似,first代表第一个元素,second代表第二个元素
cout << p[i].first << " " << p[i].second;
}
array 数组
tuple 元组
\(tuple\) 模板是 \(pair\) 的泛化,可以封装不同类型任意数量的对象。
可以把 \(tuple\) 理解为 \(pair\) 的扩展,\(tuple\) 可以声明二元组,也可以声明三元组。
\(tuple\) 可以等价为结构体使用
#include <tuple> //头文件
- 声明初始化
tuple<int, int, string> t1;//声明一个空的 \(tuple\) 三元组
t1 = make_tuple(1, 1, "hahaha");//赋值
tuple<int, int, int, int> t2(1, 2, 3, 4);//创建的同时初始化
可以使用pair对象构造tuple对象,但tuple对象必须是两个元素
auto p = make_pair("wang", 1);
tuple<string, int> t3 {p}; //将pair对象赋给tuple对象
- 元素操作
int first = get<0>(t);//获取 \(tuple\) 对象 \(t\) 的第一个元素
get<0>(t) = 1;//修改 \(tuple\) 对象 \(t\) 的第一个元素
- 函数操作
- 获取元素个数
tuple<int, int, int> t(1, 2, 3);
cout << tuple_size<decltype(t)>::value << "\n"; // 3
- 获取对应元素的值
通过 get<n>(obj) 方法获取,\(n\) 必须为数字不能是变量
tuple<int, int, int> 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<int, string, int> t(1, "hahaha", 3);
tie(one, two, three) = t;
cout << one << two << three << "\n"; // 1hahaha3
用户:huangyuze1919810
密码:Huangyuze114514
邮箱:hunagyuze114514@qq.com

浙公网安备 33010602011771号