小知识
指针常量和常量指针
c++的异常处理
//捕获和处理异常
//1.throw 抛出异常,(可以理解为返回值,值是任何类型,是我们处理异常的一个参照)
//2.try(检查,捕获)和catch(处理异常)
//基本写法(try和catch必须一起出现,它们之间的{}不能省略)
try
{
正常需要检查是否存在异常代码
}
catch (类型)//理解为switch的case
{
处理是根据抛出的数据类型决定如何处理
}
一个try可以对应多个catch,执行机制与if和else if的机制一样,一次只能执行一个匹配项
#include<iostream>
using namespace std;
int divisor(int a, int b) {
if (b == 0)//异常
throw 0;//抛出
return a / b;
}
void print(int a, int b) {
cout << divisor(a, b);
}
void test01() {
print(1, 0);//引发了异常,但没有做处理
}
void test02() {
try {
print(1, 0);
}
catch (int) {
cout << "除数不能为零" << endl;
}
}
//删减符... catch(...)任何类型的异常都捕获
void test03() {
try {
print(1, 0);
}
catch (...) {
cout << "除数不能为零" << endl;
}
}
int main(){
//test01();
//test02();
test03();
return 0;
}
基础算法
排序
排序的时间复杂度
快速排序
快速排序
思想:分治
流程:
1.确定分界点:q [ l ] q[l]q[l]、q [ ( l + r ) / 2 ] q[(l+r)/2]q[(l+r)/2]、q[r] q[r]q[r]、随机点,记值为 x
2.调整区间为,分界点左边的数都 <= x,分界点右边的数都 >= x
3.递归处理左右两段
···心得:建议背一个模板
http://t.csdn.cn/hkoVU(具体的画图分析快速排序)
模板:
void quick_sort(int q[], int l, int r)
{
if (l >= r)return;
int x = q[l+r>>1], i = l - 1, j = r + 1;
while (i < j)
{
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j)
{
swap(q[i], q[j]);
}
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
归并排序
归并排序
思想:分治![IMG_20230306_191601]
流程:
1.确定分界点:m i d = ( l + r )/2
2.递归排序 left, right 部分
3.归并:合二为一
void merge_sort(int q[], int l, int r)
{
int temp[N];
if (l >= r)return;//递归边界,如果长度为零直接返回
int k = 0, mid = (l + r) >> 1;//取分界点
merge_sort(q, l, mid), merge_sort(q, mid + 1, r);//递归左半部分,右半部分进行排序
int i = l, j = mid + 1;
while (i <= mid && j <=r)//左半部分和右半部分最小值进行比较
{
if (q[i] <= q[j])temp[k++] = q[i++];
else temp[k++] = q[j++];
}
while (i <= mid)temp[k++] = q[i++];
while (j <= r)temp[k++] = q[j++];
for (int i = l, j = 0; i <=r; i++, j++)q[i] = temp[j];
}
高精度
加法
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
vector<int> add(vector<int>& A, vector<int>& B)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || i < B.size() ; i++)
{
if (i < A.size()) t += A[i];
if (i < B.size())t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(1);
return C;
}
int main()
{
string a, b;
vector<int> A, B;
cin >> a >> b;
for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
auto C = add(A, B);
for (int i =C.size() - 1 ; i >=0 ; i--)
{
cout << C[i];
}
}
前缀和
差分
#incude<iostream>
using namespace std;
const int N=1e6+10;
int n,m;
int a[N],b[N];//a是原数组及前缀和,b是差分数组。差分数组和原数组互为逆运算。
//在初始化时,我们可以理解为在0数组上,依次插入一个c = A[i]
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
scanf(“%d%d”,&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) insert(i,i,a[i]);//第一次insert求的是a的差分数组,可以写写看看。
//比如b是1,2,3,4,5,6
//那么a是1,3,6,10,15,21;
//a是b的前缀和数组,b是a的差分数组
//这里的insert求的是a的差分数组,刚开始b数组的初始化全为0,a数组的数据题目已经给出;
//例如第一次插入 b[1]=1,b[2]=0-1=-1;那么继续循环第二次插入,b[2]=-1+3=2,b[3]=0-3=-3;
//继续循环b[3]=-3+6=3。如此循环可以看出,a的差分数组慢慢求出
while(m--)//这里没什么好说的就是m个询问
{
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);//这里的插入才是为了解答题目
}
for(int i=1;i<=n;i++) a[i]=a[i-1]+b[i];//这里y总为了省事,直接用的差分数组存储,我修改了下。
//a[i]表示b数组前i个数的和,那么就是b数组前i-1个数的和加上第i个数。所以a[i]=a[i-1]+b[i];
//注意此时求出的前缀和数组是加上数之后的前缀和数组。最后输出a[i]
for(int i=1;i<=n;i++) printf("%d ",a[i]);
//这里提醒一点,前缀和和差分数组下标都是从1开始,为了方便。
return 0;
}
//y总模板
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=100010;
int a[N],b[N];
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) insert(i,i,a[i]);
while(m--)
{
int l, r ,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
//最后两次循环y总的模板
for(int i=1;i<=n;i++)b[i]+=b[i-1];//这里求的是b数组的前缀和,相当于a数组
for(int i=1;i<=n;i++)cout<<b[i];//经过上面一次循环,b数组已经相当于a数组了,此时只需要输出b数组即可
return 0;
}
差分矩阵
#include<bits/stdc++.h>
using namespace std;
int n,m,q;
const int N=1010;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1]+=c;
b[x1][y2+1]-=c;
b[x2+1][y1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
insert(i,j,i,j,a[i][j]);
}
}
while(q--)
{
int x1,y1,x2,y2,c;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+b[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
printf("%d ",a[i][j]);
}
puts(" ");
}
}
#include<iostream>
using namespace std;
const int N =1010;
int n, m, q, a[N][N], b[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c; //整个矩阵
b[x2 + 1][y1] -= c; // x2之后(长x2+1...右端,宽y1的长方形)【高】[红色区域]
b[x1][y2+ 1] -= c; //y2之后 (长x1..右端, 宽y2 + 1..的长方形) 【短粗胖】[绿色区域]
b[x2 + 1][y2 + 1] += c; // 右下角小矩阵,从x2+1, y2+ 1往右下的矩形
// 原本O(N) 现在就4*O(1)= O(1). 不用操作矩阵里每个数,只用改这四个。
}
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
insert(i, j, i, j, a[i][j]); //同理,根据上述矩阵分析,a[i][j]+c <=> b(i,j) 的那些操作。如果矩阵塌缩为点,即在b(i,j)插入了a(i,j)的值
// 想象a(i,j) + c的运算a(i,j)设为0,c=(a(i,j))即通过insert函数b(i,j)表达了0到a(i,j)的信息转换。这次insert后b(i,j)的前缀和=a(i,j)
while(q--)
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1, y1, x2, y2, c);
}
for(int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1]; //bij前缀和定义 = aij;
// 即x方向(横着看)+ (i-1,j)和之前的,
// y方向(竖着看)+ (i, j - 1)和之上的。由于多加了一倍重合的(i-1, j-1)往前往上的部分,所以减回来。总运算即为(2D二维前缀和)
//对于矩阵坐标不熟的可以背一下
for (int i = 1;i <= n; i++){
for (int j = 1; j <= m; j++)
printf("%d ", b[i][j]); //以上操作过后b(i,j)变成了前缀和,即为a(i,j)
puts("");
}
}
作者:yxc
链接:https://www.acwing.com/video/244/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二分算法
整数二分
int l=0,r=n-1;
int x;//定义分界点
cin>>x;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<=x)l=mid;
else r=mid-1;
}
while(l<r)
{
int mid=l+r>>1;
if(q[mid]>=x)r=mid;
else l=mid+1;
}
#include<bits/stdc++.h>
using namespace std;
#define N 100000
int q[N],m,n;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)cin>>q[i];
while(m--)
{
int x;
cin>>x;
int l=0,r=n-1;
while(l<r)
{
int mid=l+r>>1;
if(q[mid]>=x)r=mid;
else l=mid+1;
}
if(q[l]!=x)cout<<"-1 -1"<<endl;
else
{
cout<<l<<" ";
int l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<=x)l=mid;
else r=mid-1;
}
cout<<r<<endl;
}
}
}
浮点二分
双指针算法
模板
for(int i=0nj=0;i<n;i++)
{
while(j<i&&check(i,j))j++;
//每道题的题目逻辑
}
#include <iostream>
using namespace std;
const int N = 100010;
int n;
int q[N], s[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
int res = 0;
for (int i = 0, j = 0; i < n; i ++ )
{
s[q[i]] ++ ;
while (j < i && s[q[i]] > 1) s[q[j ++ ]] -- ;
res = max(res, i - j + 1);
}
cout << res << endl;
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/40066/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
所需要学的例题都在本文件夹里的双指针算法pdf中
位运算
位运算的笔记
https://www.runoob.com/w3cnote/bit-operation.html
#include<iostream>
#include <bitset>
using namespace std;
int main()
{
int n=5;
int m = -5;
cout << bitset<32>(n) << endl;
cout << bitset<32>(n >> 1) << endl;
cout << bitset<32>(n << 1) << endl;
cout << bitset<32>(m) << endl;
cout << bitset<32>(m >> 1) << endl;
cout << bitset<32>(n << 1) << endl;
}
公式一:看n的二进制表示第k'位的数
- n>>k&1;
//查看某个数的二进制表示
int main()
{
int n=10;
for(int k=3;k>=0;k--)
{
cout<<(n>>k&1);
}
return 0;
}
离散化
模板
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; // 映射到1, 2, ...n
}
区间合并
数据结构
单链表
头插
//head 表示头结点的下标
//e[i] 表示节点i的值
//ne[i] 表示节点i的next指针
//idx 存储当前已经用到了那个点
int head, e[N], ne[N], idx;
//初始化
void init()
{
head = -1;
idx = 0;
}//头插
void add_head(int n)
{
e[idx] = n;
ne[idx] = head;
head = idx;
idx++;
}
任意位置的后面插入
//将x插入下标是k的后面
void add(int k, int x)
{
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx++;
}
删除k后面一点的位置
//删除k点后面的点
void remove(int k)
{
ne[k] = ne[ne[k]];
}
双链表
初始化
//初始化
void init()
{
//0表示左端点,1表示右端点
r[0] = 1, l[1] = 0;
idx = 2;
}
插入
//将x插入下标是k的后面(右边)
void add(int k, int x)
{
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
}
//左边
add(l[k],x);
删除
//删除k点
void remove(int k)
{
r[l[k]] = r[k];
l[r[k]] = l[k];
}
栈和队列
#include<iostream>
using namespace std;
const int N = 10010;
//tt 栈顶下标
int stk[N], tt;
//插入一个
skt[++ tt] = x;
//弹出
tt--;
//判断栈是否为空
if (tt > 0) //不空
else //空
//栈顶
skt[tt]
**********************************
//队列
//在队尾插入元素,在队头弹出元素(先进先出)
int q[N], hh, tt=-1;//hh 队头,tt 队尾
//插入
q[++tt] = x;
//弹出
hh++;
if(hh<=tt) //不空
else//空
//取出队头元素
q[hh]
//取出队尾元素
q[tt]
模拟栈
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N =100010;
int skt[N],tt;
int main()
{
int m;
cin>>m;
while(m--)
{
string op;
int x;
cin>>op;
if(op=="push")
{
cin>>x;
skt[++tt]=x;
}
else if(op=="pop")
{
tt--;
}
else if(op=="empty")
{
if(tt>0)
{
cout<<"NO"<<endl;
}
else
{
cout<<"YES"<<endl;
}
}
else
{
cout<<skt[tt]<<endl;
}
}
}
模拟队列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N =100010;
int skt[N],tt;
int main()
{
int m;
cin>>m;
while(m--)
{
string op;
int x;
cin>>op;
if(op=="push")
{
cin>>x;
skt[++tt]=x;
}
else if(op=="pop")
{
tt--;
}
else if(op=="empty")
{
if(tt>0)
{
cout<<"NO"<<endl;
}
else
{
cout<<"YES"<<endl;
}
}
else
{
cout<<skt[tt]<<endl;
}
}
}
单调栈
#include<iostream>
using namespace std;
const int N = 10010;
int skt[N],tt;
int n;
int main()
{
cin >> n;
while (n--)
{
int x;
cin >> x;
while (tt && skt[tt] >= x)tt--;
if (tt) cout << skt[tt] << " ";
else cout << "-1 ";
skt[++tt] = x;
}
}
单调队列(https://www.bilibili.com/video/BV1H5411j7o6?spm_id_from=333.337.search-card.all.click)//B站链接
KMP
暴力算法
朴素算法
s[N],p[M]//s是总的字符串,p是小的模板连
for (int i = 1; i <= n; i ++ )
{
bool flag = true;
for (int j = 1; j <= m; j ++ )
{
if (s[i + j - 1] != p[j])
{
flag=false;
break;
}
}
}
模板
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = ne[j];
// 匹配成功后的逻辑
}
}
Tire
模板
int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串
void insert(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if (!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
cnt[p] ++ ;
}
// 查询字符串出现的次数
int query(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
char str[N];
int son[N][26], cnt[N], idx;
/*insert()函数:对输入的字符串进行遍历,逐个字符插入到字典树中。如果当前节点没有对应的子节点,则创建一个新的节点,并更新节点计数。最后,将指针指向下一个节点。*/
void insert(char *str) // 插入字符串
{
int p=0;
for(int i=0;str[i];i++)
{
int u=str[i]-'a';
if(!son[p][u]) son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;
}
/*query()函数:对输入的字符串进行遍历,逐个字符在字典树中查询。如果当前字符没有对应的子节点,则说明该字符串不存在于字典树中,返回0。否则,将指针指向下一个节点,直到字符串遍历完成。返回最后一个节点的计数值。*/
int query(char *str)
{
int p=0;
for(int i=0;str[i];i++)
{
int u=str[i]-'a';
if(!son[p][u]) return 0;
p=son[p][u];
}
return cnt[p];
}
int main()
{
int n;
cin>>n;
while (n -- )
{
char op[2];
scanf("%s%s",op,str);
if(op[0]=='I')insert(str);
else printf("%d\n",query(str));
}
return 0;
}
并查集
1.将两个集合合并
2.询问两个元素是否在一个集合当中
基本原理:
每一个集合都用一棵树来表示,树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点
问题1:如何判断树根:if(p[x]==x)
问题2:如何求x的集合编号:while(p[x]!=x) x=p[x];
问题3:如何合并两个集合:px是x的集合编号,py是y的集合编号。p[x]=y.
模板
(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
(3)维护到祖宗节点距离的并查集:
int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
堆
模板
// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;
// 交换两个点,及其映射关系
void heap_swap(int a, int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a], hp[b]);
swap(h[a], h[b]);
}
void down(int u)
{
int t = u;
if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if (u != t)
{
heap_swap(u, t);
down(t);
}
}
void up(int u)
{
while (u / 2 && h[u] < h[u / 2])
{
heap_swap(u, u / 2);
u >>= 1;
}
}
// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);
哈希
模板
一般哈希
(1) 拉链法
int h[N], e[N], ne[N], idx;
// 向哈希表中插入一个数
void insert(int x)
{
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx ++ ;
}
// 在哈希表中查询某个数是否存在
bool find(int x)
{
int k = (x % N + N) % N;
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == x)
return true;
return false;
}
(2) 开放寻址法
int h[N];
// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
int find(int x)
{
int t = (x % N + N) % N;
while (h[t] != null && h[t] != x)
{
t ++ ;
if (t == N) t = 0;
}
return t;
}
字符串哈希
/*核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果*/
typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
STL
vector, 变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back()
push_back()/pop_back()
begin()/end()
[]
支持比较运算,按字典序
pair<int, int>
first, 第一个元素
second, 第二个元素
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
string,字符串
size()/length() 返回字符串长度
empty()
clear()
substr(起始下标,(子串长度)) 返回子串
c_str() 返回字符串所在字符数组的起始地址
queue, 队列
size()
empty()
push() 向队尾插入一个元素
front() 返回队头元素
back() 返回队尾元素
pop() 弹出队头元素
priority_queue, 优先队列,默认是大根堆
size()
empty()
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出堆顶元素
定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
tack, 栈
size()
empty()
push() 向栈顶插入一个元素
top() 返回栈顶元素
pop() 弹出栈顶元素
deque, 双端队列
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
size()
empty()
clear()
begin()/end()
++, -- 返回前驱和后继,时间复杂度 O(logn)
set/multiset
insert() 插入一个数
find() 查找一个数
count() 返回某一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
lower_bound()/upper_bound()
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find()
[] 注意multimap不支持此操作。 时间复杂度是 O(logn)
lower_bound()/upper_bound()
unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
和上面类似,增删改查的时间复杂度是 O(1)
不支持 lower_bound()/upper_bound(), 迭代器的++,--
bitset, 圧位
bitset<10000> s;
~, &, |, ^
>>, <<
==, !=
[]
count() 返回有多少个1
any() 判断是否至少有一个1
none() 判断是否全为0
set() 把所有位置成1
set(k, v) 将第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反