注:部分陈述来自menci dalao,实在懒得写陈述。
注:蒟蒻整理,有些名词使用不规范,请谅解。
一. 队列(queue) 先进先出的执的排列
STL 在头文件 中提供了先入先出队列 queue。
使用 push() 向队列中加入元素。
使用 front() 获取队首元素(并不删除)。
使用 pop() 删除队首元素。
使用 empty() 判断队列是否为空。
很常用很熟练,就不写代码举例了。
二. 优先队列(priority_queue)
STL 在头文件 中提供优先队列 priority_queue,在任意时间都能取出队列中的最大值。
使用 push() 向优先队列中加入元素,其时间复杂度为O(logn)。
使用 top() 获取优先队列中最大的元素(并不删除),其时间复杂度为O(1)。
使用 pop() 删除优先队列中最大元素,其时间复杂度为O(logn)。
使用 empty() 判断优先队列是否为空。
priority_queue<T> q;//T为想要定义的类型
也可以定义为优先最小值:
priority_queue< T,vector<T>,greater<T> >q;//注意greater<int>后面要加空格。 否则计算机会当右移运算符.
三. 双队列(deque) 连续存储的指向不同元素的指针所组成的数组
感觉用不到,
具体看链接吧
四. 栈(stack) 后进先出的值的排列
STL 在头文件 提供了后入先出栈 stack。
使用 push() 向栈中加入元素。
使用 top() 获取栈顶元素(并不删除)。
使用 pop() 删除栈顶元素。
使用 empty() 判断栈是否为空。
std::stack<int> s;
bool flag = s.empty();
// flag = true,栈初始为空
s.push(23333);
s.push(66666);
while (!s.empty()) {
printf("%d\n", s.top());
s.pop();
}
// 依次输出 66666,23333
一个栈的使用模板:
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
stack<int> st;
int a[11];
int main()
{
for(int i=1;i<=10;i++)
{
cin>>a[i];
st.push(a[i]);//入栈
}
st.pop();//弹栈, 后入先弹
while(!st.empty())//判断是否为空
{
int k;
k=st.top();//访问栈顶
st.pop();//记得弹栈,否则死循环
cout<<k;
if(st.size()<5) //求个数
break;
}
}
比较常用,不再举例。
五. 向量(vector) 连续存储的元素
STL 在头文件 提供了一个可变长的数组 vector,它支持动态的插入、删除操作。
以下代码声明了一个 vector,它的每个元素类型为 int,初始元素数量为 0。
vector<int> v;
以下代码声明了一个 vector,它的每个元素类型为 int,初始元素数量为 n。
std::vector<int> v(n);
vector 提供 begin() 和 end(),分别获取指向第一个元素和最后一个元素之后的迭代器。
需要注意的是,迭代器的头是第0位
以下代码对 v 中的所有元素以升序排序:
std::sort(v.begin(), v.end());
使用 size() 得到 vector 的元素数量,使用 resize() 重新指定 vector 的元素数量。
分别使用 push_back() 和 pop_back() 在 vector 的尾部加入或删除元素,这两个过程的时间复杂度为O(1)O(1)。
使用 insert() 在某个特定的位置插入一个元素,时间复杂度为O(n)。
使用 erase() 删除某个位置的元素,时间复杂度为O(n)。
vector<int> v;
// v.size() = 0
v.push_back(23333);
// v.size() = 1, v = { 23333 }
v.insert(v.begin() + 0, 890);
// v.size() = 2, v = { 890, 23333 }
v.insert(v.begin() + 1, 12345);
// v.size() = 3, v = { 890, 12345, 23333 }
v.erase(v.begin() + 0);
// v.size() = 2, v = { 12345, 23333 }
for (nt i = 0; i < v.size(); i++) {
printf("%d\n", v[i]);
}
// 依次输出 12345、23333
v.pop_back();
// v.size() = 1, v = { 12345 }
注意,在加入元素时,如果 vector 拥有的内存空间不足以存放欲加入的元素,则 vector 会申请一块新的内存,并将旧数据拷贝过去,这个过程通常花费 O(n)O(n) 的时间。
本蒻用vector不太熟练,所以一遍不会用,怕炸;
但最近有个t,stl跑一跑就a了
题目在这,T1
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
vector<string> a;
string aa;
int ans,n;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)cin>>aa,a.push_back(aa);
sort(a.begin(),a.end());
vector<string>::iterator new_end;
new_end=unique(a.begin(),a.end());
for(vector<string>::iterator i=a.begin();i!=new_end;i++)ans++;printf("%d\n",ans);
for(vector<string>::iterator i=a.begin();i!=new_end;i++)cout<<*i<<endl;
}
六. 映射(map) 由{键,值}对组成的集合
map的用法有好多,可以用来离散化,记录等,它的操作一般应该都是log n的;
比如:根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。
map < vector,int> q; //映射不定长数组,神奇QAQ ——————T2
关于map的离散化:
再举个例子
#include<iostream>
#include<map>
#include<cstring>
#include<string>
using namespace std;
map<int,string> q;
map<string,int> p;
int main()
{
q[1]="abcd";
p["abcd"]=1;
cout<<q[1];
cout<<endl;
cout<<p["abcd"];
}
输出:
map的用法比较熟了,不举其他例子了。
七. 多重映射(multimap) 允许键对有相等的次序的映射
八. 集合(set) 由节点组成的红黑树,每个节点都包含着一个元素
这里就用代码解释
[set的遍历与两种排序]
#include<iostream>
#include<set>
using namespace std;
int main()
{
//set<int,greater<int> > s; ====>从大到小排序
set<int> s;.//默认从小到大
s.insert(5); //第一次插入5,可以插入
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(5); //第二次插入5,重复元素,不会插入
set<int>::iterator it; //定义前向迭代器
//中序遍历集合中的所有元素
for(it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
[set结构体重定义]
struct Info
{
string name;
double score;
bool operator < (const Info &a) const // 重载“<”操作符,自定义排序规则
{
//按score由大到小排序。如果要由小到大排序,使用“>”即可。
return a.score < score;
}
};
int main()
{
set<Info> s;
Info info;//开成info[4]也可以
//插入三个元素
info.name = "Jack";//info[1].name="Jack"
info.score = 80;//info[1].score="80"
s.insert(info);//s.insert(info[1])
info.name = "Tom";
info.score = 99;
s.insert(info);
info.name = "Steaven";
info.score = 60;
s.insert(info);
set<Info>::iterator it;
for(it = s.begin(); it != s.end(); it++)
cout << (*it).name << " : " << (*it).score << endl;
return 0;
}
/*
运行结果:
Tom : 99
Jack : 80
Steaven : 60
*/
[set删除与查找]
int main()
{
set<int> s;
s.insert(5); //第一次插入5,可以插入
s.insert(1);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(5); //第二次插入5,重复元素,不会插入
s.erase(3); //删除键值为3的元素
set<int>::iterator it;//申请一个迭代器,注意stl迭代器的申请方法
it = s.find(6); //查找键值为6的元素存不存在
if(it != s.end())
cout << *it << endl;
else
cout << "not find it" << endl;
it = s.find(3);
if(it != s.end())
cout << *it << endl;
else
cout << "not find it" << endl;
//查找元素存在的位置
int pos2=*s.lower_bound(2);//返回第一个大于等于key_value的定位器(1,2,3,4,5中会返回2;1,3,4,5中会返回3)
int pos2_next=*s.upper_bound(2);//返回第一个大于key_value的定位器(1,2,3,4,5中会返回3)
cout<<pos2<<endl;
cout<<pos2_next;
return 0;
}
/* 也可以用下面这种方式查询
#include <cstdio>
#include <set>
using namespace std;
int main() {
set <int> s;
int a;
for(int i = 0; i < 10; i++)
s.insert(i);
for(int i = 0; i < 5; i++) {
scanf("%d", &a);
if(!s.count(a)) //不存在
printf("does not exist\n");
else
printf("exist\n");
}
return 0;
}//s.count 记录的是元素出现的次数, 因为在集合中, 最多出现一次, 所以可用此查询
*/
[set重定义比较]
struct mycomp
{ //自定义比较函数,重载“()”操作符
bool operator() (const int &a, const int &b)
{
if(a != b)
return a > b;
else
return a > b;
}
};
int main()
{
set<int, mycomp> s; //采用比较函数mycomp
s.insert(5); //第一次插入5,可以插入
s.insert(1);
s.insert(6);
s.insert(3);
s.insert(5); //第二次插入5,重复元素,不会插入
set<int,mycomp>::iterator it;
for(it = s.begin(); it != s.end(); it++)
cout << *it << " ";
cout << endl;
return 0;
}
/*
运行结果:6 5 3 1
*/
九. 多重集合(multiset) 允许存在两个次序相等的元素的集合
与set用法一样,详细的,请上翻。
区别就是multiset 允许存在两个次序相等的元素的集合
申请:
multiset<int> a;
十. 列表(list) 由节点组成的双向链表,每个结点包含着一个元素
还没用到过,手写数组就好了
十一. sort
复杂度是 nlogn (log以2为底),类似于快排。 ※很常用的函数。
sort的功能很强大:
它可以对各种类型排序,只需要写一下自己的排序方式cmp就可以排各种序(vector,struct,string等),
1.需要提醒的是,对于字符串而言,sort默认的排序方式就是按照字典序。
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s[10];
cin>>s[1]>>s[2]>>s[3]>>s[4]>>s[5];
sort(s+1,s+1+5);
for(int i=1;i<=5;i++) cout<<s[i]<<" ";
}
结果:
2.sort默认由小到大排序,但也可以用封装好的从大到小排序
less<数据类型>()//从小到大排序
greater<数据类型>()//从大到小排序
无需定义,在头文件algorithm中
sort(a,a+10,greater<int>()); sort(a,a+10,greater<char>());
sort(a.begin(),a.end()); //对于stl的容器来说
3.对结构体等排序的cmp写法
struct node{int x,y;}e[maxn];
bool cmp(node a,node b){return a.x>b.x}//node为结构体类型,按照x的降序排序
sort(e+1,e+1+n,cmp);
十二. memeset
memset实际上是用来对一段内存空间全部设置为某个字符,但是对于0,-1的值是可以直接赋的(至于为什么,蒟蒻也不知道)。
对于它的复杂度,据说如果不是赋值字符的话,也是O(n)的,不过常数很小,相对于for循环来说好用很多。
这里有一段测试代码
#include<iostream>
#include<cstring>//memset的头文件
using namespace std;
int a[20],b[20],c[20],d[20],e[20],f[20],g[20];
char ch[20];
int main()
{
memset(a,0,sizeof(a));
memset(b,-1,sizeof(b));
memset(c,1,sizeof(c));
memset(d,127,sizeof(d));
memset(e,127/3,sizeof(e));
memset(f,0x3f3f3f3f,sizeof(f));
memset(g,0x7f7f7f7f,sizeof(g));
memset(ch,'z',sizeof(ch));
for(int i=1;i<=5;i++) cout<<a[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<b[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<c[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<d[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<e[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<f[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<g[i]<<" "; cout<<endl;
for(int i=1;i<=5;i++) cout<<ch[i]<<" "; cout<<endl;
}
运行结果:
可以看到,memset赋字符好很用,(string好像不行)。
直接赋1的话,其结果已经到了10^7的级别,在int内最大得赋值控制在0x7f7f7f7f就好。而对于spfa等算法int内的最大值inf最佳的赋值是0x3f3f3f3f(和int范围一个级别,但两两相加不爆int),(127/3也可以,稍小一点),因为在松弛的时候如果memset赋的最大值是0x7f7f7f7f,即2319062143稍加上一个数就会爆int。int的最大值2^31-1=2147483647.
对于结构体的清零操作:
struct tre{ int li,ri;}deep[32];
int main()
{
deep[5].ri=99;
memset(&deep,-1,sizeof(deep));//0也可以
cout<<deep[5].ri;
}
十三. swap
swap是一个很强大的交换函数,可以交换各种类型(string,vector等,后者很麻烦,不举例)。
十四. lower_bound,upper_bound,binary_search
比较实用又比较玄学,lower_bound和upper_bound的复杂的是二分的复杂度,即logn(以2为底)。
一些容器(set,vector等)也支持lower_bound等操作。
首先要进行sort排序。
lower_bound 用于在一个升序序列中查找某个元素,并返回第一个不小于该元素的元素的迭代器,如果找不到,则返回指向序列中最后一个元素之后的迭代器。通过 -a(a是变量名)的方式获取排名。
upper_bound 用于在一个升序序列中查找某个元素,并返回第一个大于该元素的元素的迭代器,如果找不到,则返回指向序列中最后一个元素之后的迭代器。通过 -a的方式获取排名。
binary_search 用于确定某个元素有没有在一个升序序列中出现过,返回 true 或 false。
三者的例子:
#include <cstdio>
#include <algorithm>
#include<iostream>
using namespace std;
int n;
int a[10]={-5,1,3,-2,5,6,8,9,4,-10};
int main() {
sort(a,a+10);//-10,-5,-2,1,3,4,5,6,8,9
int *it1=lower_bound(a,a+10,1); cout<<*it1<<endl;//第一个大于等于1的 输出 1
int *it2=lower_bound(a,a+10,2); cout<<*it2<<endl;//输出 3
int *it3=upper_bound(a,a+10,1); cout<<*it3<<endl;//第一个大于1的 输出 3
int *it4=upper_bound(a,a+10,2); cout<<*it4<<endl;//输出 3
bool b=binary_search(a,a+10,7); cout<<b<<endl;//数组中没有7 输出0
int pos=lower_bound(a,a+10,3)-a; cout<<pos;//输出3的位置 4 (0 1 2 3 4),即第五个
}
容器中 用 container< T> :: iterator it;(其中 container 是容器类型,可以是 vector、set 等,T 是容器中元素的类型。)的方式申请迭代器,
it是container< T> :: iterator类型的数,可以与s.begin() s.end()作比较等操作。(其中s是容器类型的名称,如multiset < int> s;)。
*it指针返回查询的值。这里的it,只支持 it++ 和it - -的操作,不能it+i;
输出时,cout<<*it; 输出指向的值。
对于容器的操作,看之前做的题,T1优化后的
十五. unique
需要先排序,然后用unique进行去重操作,通过-a(a是变量名)获取数量位置
下面的代码演示了读入 n个数,并升序排序并去重后输出。
#include<iostream>
#include<vector>
#include<algorithm> //unique头文件
using namespace std;
vector<int>a;
int main()
{
for(int i=1;i<10;i++)
for(int j=0;j<3;j++)
a.push_back(i);
sort(a.begin(),a.end());
a.erase(unique(a.begin(),a.end()),a.end());
for(int i=0;i<a.size();i++)
cout<<a[i];
return 0;
}
/*也可以这样:
int count=unique(a,a+n)-a;
for (int i=0; i<count;i++)
printf("%d\n", a[i]);
*/
十六. min,max
较大值较小值,很简单也很常用
int a=1,b=99;
c=min(a,b);//c=1
d=max(a,b);//d=99