STL
STL
0. 简介
STL 即标准模板库(Standard Template Library),是 C++ 标准库的一部分,里面包含了一些模板化的通用的数据结构和算法。由于其模板化的特点,它能够兼容自定义的数据类型,避免大量的造轮子工作。NOI 和 ICPC 赛事都支持 STL 库的使用,因此合理利用 STL 可以避免编写无用算法,并且充分利用编译器对模板库优化提高效率。
1.Dynamic Arrays and Lists
(1).Vector
题意
维护动态数组 \(A\),支持以下三种操作:
- push_back(x): 在 \(A\) 的末尾加入元素 \(x\)
- random_access(p): 查询元素 \(A_p\)
- pop_back(): 删除 \(A\) 的最后一个元素
简介
vector 是 STL 提供的一种 内存连续,长度可变 的动态数组。
虽说动态数组,但 vector 的底层仍是定长数组。当数组大小不足时,vector 会倍增的申请、分配更多连续的空间。
定义
vector<int>h; 定义一个数据类型为 int 的 vector h。
需要头文件 #include<vector>。
函数
push_back(int value)在 vector 的末尾加入元素 value,均摊时间复杂度 \(O(1)\),最坏复杂度为 \(O(n)\)(倍增申请空间)。operator[]访问 vector 中的某一元素,用法与普通数组相同,时间复杂度 \(O(1)\)。pop_back()删除 vector 的最后一个元素,时间复杂度 \(O(1)\)。
代码
int n;
vector<int>h;
int main()
{
n=re();
for(int i=1;i<=n;i++)
{
int op=re();
if(op==0)h.push_back(re());
if(op==1)wr(h[re()]),putchar('\n');
if(op==2)h.pop_back();
}
return 0;
}
(2).Deque
题意
维护动态数组 \(A\),支持以下三种操作:
- push(d,x): 如果 d=0,在 \(A\) 的开头加入元素 \(x\);如果 d=1,在 \(A\) 的末尾加入元素 \(x\)
- random_access(p): 查询元素 \(A_p\)
- pop(): 如果 d=0,删除 \(A\) 的第一个元素;如果 d=1,删除 \(A\) 的最后一个元素
简介
deque 是 STL 提供的一种双端队列。
基本操作与 vector 相似。
定义
deque<int>h; 定义一个数据类型为 int 的 deque h。
需要头文件 #include<deque>。
函数
push_front(int value)在 deque 的开头加入元素 value,时间复杂度 \(O(1)\)。push_back(int value)在 deque 的末尾加入元素 value,时间复杂度 \(O(1)\)。operator[]访问 deque 中的某一元素,用法与普通数组相同,时间复杂度 \(O(1)\)。pop_front()删除 deque 的第一个元素,时间复杂度 \(O(1)\)。pop_back()删除 deque 的最后一个元素,时间复杂度 \(O(1)\)。
代码
int n;
deque<int>h;
int main()
{
n=re();
for(int i=1;i<=n;i++)
{
int op=re();
if(op==0)
{
int pd=re(),k=re();
if(pd)h.push_back(k);
else h.push_front(k);
}
if(op==1)wr(h[re()]),putchar('\n');
if(op==2)
{
int pd=re();
if(pd)h.pop_back();
else h.pop_front();
}
}
return 0;
}
(3).List
题意
维护动态链表 \(L\),支持以下三种操作:
- insert(x): 在 \(L\) 的光标所指元素之前插入元素 \(x\),随后光标指向元素 \(x\)
- move(d): 光标向后移动 \(d\) 个单位
- erase(): 删除 \(L\) 的光标所指的元素,随后光标指向下一个元素
最开始 \(L\) 为空,仅含有一个特殊的元素 END,光标指向 END。
简介
list 是 STL 提供的一种双向链表。
基本操作与 deque 相似。
定义
list<int>h; 定义一个数据类型为 int 的 list h。
需要头文件 #include<list>。
函数
insert(list<int>::iterator IT,int value)在 list 的 IT 位置插入元素 value,时间复杂度 \(O(1)\)。erase(list<int>::iterator IT)删除 list 的 IT 位置的元素,时间复杂度 \(O(1)\);并返回一个迭代器,指向下一个元素。begin()返回一个迭代器,指向 list 的第一个元素。end()返回一个迭代器,指向 list 的最后一个元素的后一个位置。
迭代器
由题意可得,迭代器(即光标)最初指向 list 的末尾:
list<int>::iterator IT=h.end();
list 重载了 ++ 和 --。以自增为例,对于数来说,就是简单的 +1;而对于迭代器而言,则是移动到下一个位置。
那么 move 操作就只需要将迭代器自增、自减即可。
代码
int n;
list<int>h;
int main()
{
n=re();
list<int>::iterator IT=h.end();
for(int i=1;i<=n;i++)
{
int op=re();
if(op==0)h.insert(IT,re()),IT--;
if(op==1)
{
int m=re();
if(m>0)while(m--)IT++;
else while(m++)IT--;
}
if(op==2&&h.size())IT=h.erase(IT);
}
for(IT=h.begin();IT!=h.end();IT++)
wr(*IT),putchar('\n');
return 0;
}
(4).Vector II
题意
维护 n 个动态数组 \(A_i\),支持以下三种操作:
- push_back(t,x): 在 \(A_t\) 的末尾加入元素 \(x\)
- dump(t): 查询 \(A_t\) 的所有元素
- clear(t): 清空 \(A_t\)
定义
vector<int>h[inf]; 定义 inf 个数据类型为 int 的 vector,其中第 i 个为 h[i]。
函数
push_back(int value)同上。size()返回一个整数,为当前 vector 中元素个数。clear()清空整个 vector。
代码
const int inf=1e3+7;
int n,m;
vector<int>h[inf];
int main()
{
n=re();m=re();
for(int i=1;i<=m;i++)
{
int op=re(),x=re();
if(op==0)h[x].push_back(re());
if(op==1)
{
int len=h[x].size();
if(len)
{
for(int j=0;j<len-1;j++)
wr(h[x][j]),putchar(' ');
wr(h[x][len-1]);
}putchar('\n');
}
if(op==2)h[x].clear();
}
return 0;
}
2.Basic Data Structures
(1).Stack
题意
维护 n 个栈 \(S_i\),支持以下三种操作:
- push(t,x): 在栈 \(S_t\) 的顶部加入元素 \(x\)
- top(t): 查询栈 \(S_t\) 的栈顶元素
- pop(t): 弹出栈 \(S_t\) 的栈顶元素
简介
stack 是 STL 提供的一种栈。
其底层容器是 deque。
定义
stack<int>h[inf]; 定义 inf 个数据类型为 int 的 stack,其中第 i 个为 h[i]。
需要头文件 #include<stack>。
函数
push(int value)在 stack 的顶部加入元素 value,时间复杂度 \(O(1)\)。top()返回一个整数,为 stack 的栈顶元素,时间复杂度 \(O(1)\)。pop()弹出 stack 的栈顶元素,时间复杂度 \(O(1)\)。
代码
const int inf=1e3+7;
int n,m;
stack<int>h[inf];
int main()
{
n=re();m=re();
for(int i=1;i<=m;i++)
{
int op=re(),x=re();
if(op==0)h[x].push(re());
if(op==1&&h[x].size())wr(h[x].top()),putchar('\n');
if(op==2&&h[x].size())h[x].pop();
}
return 0;
}
(2).Queue
题意
维护 n 个队列 \(Q_i\),支持以下三种操作:
- enqueue(t,x): 在队列 \(Q_t\) 的队尾加入元素 \(x\)
- front(t): 查询队列 \(Q_t\) 的队首元素
- dequeue(t): 弹出队列 \(Q_t\) 的队首元素
简介
queue 是 STL 提供的一种队列。
其底层容器是 deque。
定义
queue<int>h[inf]; 定义 inf 个数据类型为 int 的 queue,其中第 i 个为 h[i]。
需要头文件 #include<queue>。
函数
push(int value)在 queue 的队尾加入元素 value,时间复杂度 \(O(1)\)。front()返回一个整数,为 queue 的队首元素,时间复杂度 \(O(1)\)。pop()弹出 queue 的队首元素,时间复杂度 \(O(1)\)。
代码
const int inf=1e3+7;
int n,m;
queue<int>h[inf];
int main()
{
n=re();m=re();
for(int i=1;i<=m;i++)
{
int op=re(),x=re();
if(op==0)h[x].push(re());
if(op==1&&h[x].size())wr(h[x].front()),putchar('\n');
if(op==2&&h[x].size())h[x].pop();
}
return 0;
}
(3).Priority Queue
题意
维护 n 个优先队列 \(Q_i\),支持以下三种操作:
- insert(t,x): 在优先队列 \(Q_t\) 中插入元素 \(x\)
- get_max(t): 查询优先队列 \(Q_t\) 中的最大值
- delete_max(t): 弹出优先队列 \(Q_t\) 中的最大值
简介
priority_queue 是 STL 所提供的堆。
其底层容器为 vector。
定义
priority_queue<int>h[inf]; 定义 inf 个数据类型为 int 的 priority_queue,其中第 i 个为 h[i]。
需要头文件 #include<queue>。
函数
push(int value)在 priority_queue 中插入元素 value,时间复杂度 \(O(\log n)\)。top()返回一个整数,为 priority_queue 中的最大值,时间复杂度 \(O(1)\)。pop()弹出 priority_queue 中的最大值,时间复杂度 \(O(\log n)\)。
拓展
STL 默认的 priority_queue 是大根堆,如果要定义小根堆可以尝试以下方法:
- STL 嵌套
利用 greater 和 less 改变优先级。
需要头文件 #include<functional>。
对于普通的数组排序(sort),默认排序方式为从小到大,less 表示从小到大排序,greater 表示从大到小排序。
而对于优先队列,默认排序方式为从大到小,less 表示从大到小排序,greater 表示从小到大排序。
使用方法:
数组(以 vector 为例)
sort(a.begin(),a.end(),less<int>());//小->大
sort(a.begin(),a.end(),greater<int>());//大->小
优先队列
priority_queue<int,vector<int>,less<int> >h;//大根堆
priority_queue<int,vector<int>,greater<int> >h;//小根堆
- 重载运算符
用一个结构体重载 <,然后存入 priorit_queue。
struct data{
int a;
bool operator <(const data &b)const
{
return a>b.a;
}
};
priority_queue<data>h;
代码
const int inf=1e3+7;
int n,m;
priority_queue<int>h[inf];
int main()
{
n=re();m=re();
for(int i=1;i<=m;i++)
{
int op=re(),x=re();
if(op==0)h[x].push(re());
if(op==1&&h[x].size())wr(h[x].top()),putchar('\n');
if(op==2&&h[x].size())h[x].pop();
}
return 0;
}
(4).Splice
题意
维护 n 个链表 \(L_i\),支持以下三种操作:
- insert(t,x): 在 \(L_t\) 的末尾加入元素 \(x\)
- dump(t): 查询 \(L_t\) 的所有元素
- splice(s,t): 将 \(L_s\) 的所有元素插入到 \(L_t\) 的末尾,并清空 \(L_s\)。
函数
push_back(int value)同上。splice(list<int>::iterator IT,list L)将 \(L\) 转移到 IT 的位置。
拓展
splice(list<int>::iterator IT1,list,list<int>::iterator IT2)将 list 的 IT2 位置上的元素转移到 IT1 的位置上。splice(list<int>::iterator IT,list,list<int>::iterator begin,list<int>::iterator end)将 list 的 [begin,end) 位置的元素转移到 IT 的位置。
代码
const int inf=1e3+7;
int n,m;
list<int>h[inf];
int main()
{
n=re();m=re();
for(int i=1;i<=m;i++)
{
int op=re(),x=re();
if(op==0)h[x].push_back(re());
if(op==1)
{
list<int>::iterator IT=h[x].begin(),end_=--h[x].end();
if(h[x].size())
{
while(IT!=end_)
{
wr(*IT),putchar(' ');
IT++;
}
wr(*end_);
}putchar('\n');
}
if(op==2)
{
int y=re();
h[y].splice(h[y].end(),h[x]);
}
}
return 0;
}
3.Basic Operations
(1).Min-Max
题意
输入三个整数 \(a,b,c\),输出 \(a,b,c\) 中的最小值和最大值。
函数
min(int x,int y)返回一个整数,即 \(a,b\) 中较小的数。max(int x,int y)返回一个整数,即 \(a,b\) 中较大的数。
需要头文件 #include<algorithm>。
代码
int a,b,c;
int main()
{
a=re();b=re();c=re();
wr(min(min(a,b),c));putchar(' ');
wr(max(max(a,b),c));putchar('\n');
return 0;
}
(2).Min-Max Element
题意
维护给定的数组 \(A\),支持以下两种操作:
min(b,e)查询 \(A_{b},A_{b+1},\cdots,A_{e-1}\) 中的最小值。max(b,e)查询 \(A_{b},A_{b+1},\cdots,A_{e-1}\) 中的最大值。
函数
min_element(iterator begin,iterator end)返回一个迭代器,即 [begin,end) 中的最小值的位置。max_element(iterator begin,iterator end)返回一个迭代器,即 [begin,end) 中的最大值的位置。
需要头文件 #include<algorithm>。
代码
const int inf=1e3+7;
int n,m,a[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
m=re();
for(int i=1;i<=m;i++)
{
int op=re(),sta_=re(),end_=re();
if(op)wr(*max_element(a+sta_,a+end_)),putchar('\n');
else wr(*min_element(a+sta_,a+end_)),putchar('\n');
}
return 0;
}
(3).Count
题意
维护给定的数组 \(A\),支持以下操作:
count(b,e,k)查询 \(k\) 在 \(A_{b},A_{b+1},\cdots,A_{e-1}\) 中出现了几次。
函数
count(iterator begin,iterator end,int value)返回一个数,即 [begin,end) 中 value 出现的次数。
需要头文件 #include<algorithm>。
代码
const int inf=1e3+7;
int n,m,a[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
m=re();
for(int i=1;i<=m;i++)
{
int sta_=re(),end_=re(),k=re();
wr(count(a+sta_,a+end_,k));putchar('\n');
}
return 0;
}
(4).Lexicographical Comparison
题意
比较给定的数组 \(A,B\) 的字典序大小。
函数
lexicographical_compare(iterator begin1,iterator end1,iterator begin2,iterator end2)返回一个布尔,即 [begin1,end1) 的字典序是否小于 [begin2,end2)。
需要头文件 #include<algorithm>。
代码
const int inf=1e3+7;
int n,m,a[inf],b[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
m=re();
for(int i=0;i<m;i++)
b[i]=re();
wr(lexicographical_compare(a,a+n,b,b+m));
putchar('\n');
return 0;
}
4.Basic Modifications
(1).Reverse
题意
维护给定的数组 \(A\),支持以下操作:
reverse(b,e)翻转区间 \(A_b,A_{b+1},\cdots,A_{e-1}\)。
函数
reverse(iterator begin,iterator end)翻转区间 [begin,end)。
需要头文件 #include<algorithm>。
代码
const int inf=1e3+7;
int n,m,a[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
m=re();
for(int i=1;i<=m;i++)
{
int sta_=re(),end_=re();
reverse(a+sta_,a+end_);
}
for(int i=0;i<n-1;i++)
wr(a[i]),putchar(' ');
wr(a[n-1]),putchar('\n');
return 0;
}
(2).Rotate
题意
维护给定的数组 \(A\),支持以下操作:
rotate(b,m,e)对于整数 k(\(b\le k<e\)),将 \(A_{k}\) 移动到 \(A_{(k+(e-m))\bmod(e-b)}\)。
函数
rotate(iterator begin,iterator mid,iterator end)将区间 [begin,mid) 转移到区间 [mid,end) 之后。
需要头文件 #include<algorithm>。
至于为什么叫 rotate,将整个区间拼接成环试试?
代码
const int inf=1e3+7;
int n,m,a[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
m=re();
for(int i=1;i<=m;i++)
{
int sta_=re(),mid_=re(),end_=re();
rotate(a+sta_,a+mid_,a+end_);
}
for(int i=0;i<n-1;i++)
wr(a[i]),putchar(' ');
wr(a[n-1]),putchar('\n');
return 0;
}
(3).Swap
题意
维护给定的数组 \(A\),支持以下操作:
swap_range(b,e,t)对于整数 k(\(0\le k<(e-b)\)),交换 \(A_{b+k}\) 和 \(A_{t+k}\)。
函数
swap_range(iterator begin1,iterator end1,iterator begin2)交换区间 [begin1,end1) 和区间 [begin2,begin2+end1-begin1)。
需要头文件 #include<algorithm>。
拓展
swap(int a,int b) 用于交换两个整数 \(a,b\) 的值。
swap(iterator a,iterator b) 用于交换两个迭代器。
swap(container a,container b) 用于交换两个容器。
代码
const int inf=1e3+7;
int n,m,a[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
m=re();
for(int i=1;i<=m;i++)
{
int sta1=re(),end1=re(),sta2=re();
swap_ranges(a+sta1,a+end1,a+sta2);
}
for(int i=0;i<n-1;i++)
wr(a[i]),putchar(' ');
wr(a[n-1]),putchar('\n');
return 0;
}
(4).Unique
题意
对于给定的非严格单调的数组 \(A\),删除一些元素,使得 \(A\) 中包含的元素仅剩一个。
函数
unique(iterator begin,iterator end)将区间 [begin,end) 中相邻且相同的元素移动到区间末尾;并返回一个迭代器,指向压缩后的序列末尾。
需要头文件 #include<algorithm>。
代码
const int inf=1e5+7;
int n,a[inf];
int main()
{
n=re();
for(int i=0;i<n;i++)
a[i]=re();
n=unique(a,a+n)-a;
for(int i=0;i<n-1;i++)
wr(a[i]),putchar(' ');
wr(a[n-1]),putchar('\n');
return 0;
}

浙公网安备 33010602011771号