大部分内容来自sys大佬的课件+网络,侵删
【复制粘贴真爽】
算法
算法库
#include<algorithm>
序列操作
区间一般是左闭右开
unique 去重
复杂度O(n)
返回值是指针,指向去重后序列最后一项的后一项;
重复的元素会被放到后面
只能去掉连续的重复元素,要先排序
next_permutation 排列
复杂度O(n)(单次操作!)。
把序列变成下一个排列。
如果已经是最后一个排列则返回false。
reverse 反转
复杂度O(n)
该函数可以反转一段序列的元素;
例如reverse(a+2,a+5)就可以反转数组a从下标2到下标4的元素了。
从来没用过。。
swap
swap(a,b)
交换a,b ab的类型需要相同
排序操作
sort 排序
复杂度平均O(NlogN)
sort(a,a+n)即可对数组a进行升序排序操作。
可以加一个比较函数cmp,实现按照给定关键字和顺序排序
sort(a+1,a+n+1,cmp)
stable_sort
复杂度O(NlogN)
用法基本和sort相同
内部实现为归并排序,稳定排序
最大最小
min/max
min(a,b) 返回较小值
可以重载<运算符或者传入cmp min(a,b,cmp)
据说手写文本替换更快
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
min_element/max_element
复杂度O(r-l)。
返回区间最大/最小值的指针。
*p = min_element(a+l,a+r);
没用过。。
二分查找
lower_bound/upper_bound
返回不小于/大于给定值的第一个值的指针。
获取数组下标时,可用
int *p = lower_bound(a, a+6, 4); i = p – a;
则i是数组下标。
binary_search
复杂度O(logN)
在一段升序区间中查找是否存在该元素。
实现了二分查找。
用法:binary_search(a,a+n,val);//val是要查找的值
总结规律:
1、关系到区间的操作一般都是传入指针,而且是左闭右开区间。
2、关系到比较的操作一般都可以传入比较函数。
3、这些算法的速度都是比较快的,因为它们的内部实现加了很多优化。
指针 迭代器有关
指针
int* p 定义一个指针变量,变量名是p,p存储位置
输出 *p 获得p指向的值
lower_bound()的返回值类似指针
&是取址运算符,能够获取某变量的地址
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[10]={0,11,22,33,44};
int* p=a;/*等价于 int* p=&a[0];*/
cout<<p<<endl;//指针存储地址
cout<<&p<<endl;//对指针取址,返回指针的地址
cout<<*p<<endl;//指针所指向的地址上的值
cout<<a+1<<endl;/*等价*/cout<<&a[1];
int p=lower_bound(a,a+9,44)-a;
cout<<p<<endl; //下标
int p=*lower_bound(a,a+9,44);
cout<<p<<endl;//数值
int* p=lower_bound(a,a+9,44);
cout<<p<<endl;//地址
cout<<*p<<endl;//数值
cout<<p-a<<endl;//下标
}
迭代器
大部分容器都有begin()和end()两个函数,它们返回的是容器的头迭代器和尾迭代器。
要注意的是end()指向的是容器末尾元素的下一个元素,这与STL左闭右开的习惯是一致的。
对于区间相关的函数来说,只要能传入数组的指针就能传入迭代器。
相应地,返回指针的函数(例如unique、lower_bound)在此时就会返回迭代器。
sort一个vector v;
sort(v.begin(),v.end());
重载运算符
在结构体外部重载
struct point{
int x,y;
};
point operator + (point a,point b){
return (point){a.x+b.x,a.y+b.y};
}
在结构体内部重载
struct point{
int x,y;
point operator + (point b){
return (point){x+b.x,y+b.y};
}
};
外部重载 < 用于排序或容器内使用
bool operator < (node a,node b){
return a.x<b.x;
}
容器
容器的成员函数的调用方法是
容器变量名.函数名(参数1,参数2,…);
string
操作
头文件 < string >
只要包含任意c++头文件(如< iostream >)即可
定义 string s;可以看做一个动态申请内存的char数组s[]
string s1; //定义一个字符串s1,并初始化为空
string s2(s1); //用s1初始化s2
string s3(“value”); //将s3初始化为”value”
strng s4(n,’c’); //将s4初始化为字符’c’的n个副本
可以直接进行赋值操作:s=“233233”;等号右边可以是字符数组。
可以直接进行字符串加法:string a=s1+s2;
也可以直接用<、>、==等运算符比较字典序大小,可以直接sort按照字典序排序
可以用s[i]访问s的第i号字符(下标从0开始)。
在末尾插入元素 s.push_back(c);
可以对已经使用过的位置直接修改
可以直接用cin>>s的方法直接读入一个字符串。//貌似只能用cin、cout
忽略开头的(制表符、换行符、空格),当再次碰到空字符就停止(并不会读取空字符)。
卡读入的话可以用scanf读入一个字符数组再赋值给string。
getline(cin,s); cin指的是读入流,一般情况下我们直接写cin即可,s是字符串,即我们读入的东西要存放的字符串。可以读取一行(包括空格),会舍弃换行符
函数
s. size()/s.length(); //返回s的长度。
s.push_back(c); //在再s的最后插入字符c。char类型c
s.empty(); //判断s是否为空。
s.clear(); //清空s。
s.c_str(); //返回对应相等的字符数组。
s.substr(pos,len); //返回起始于pos下标处长度为len的子串。可以用strcpy赋值给char数组
s.insert(pos,c); //在pos处插入字符或字符串c string类型c
s.erase(pos,len); //删除s.substr(pos,len)。
s.find(str); //在s中寻找str这个字符串,返回该串起始位置的迭代器,复杂度O(s.size())(听说比手动查找快……) //迭代器不会用。。。
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
s="abcdefghijklmn";
cout<<s.size()<<endl;
char a='o';
s.push_back(a);
s.push_back('o');
cout<<s<<endl;
char ss[5000];
//printf("%s",s); 会gg
printf("%s\n",s.c_str());
cout<<s.substr(1,2)<<endl;
string p="a";
s.insert(1,"a");
s.insert(1,p);
cout<<s<<endl;
s.erase(0,2);
cout<<s<<endl;
s.push_back('a'); 插入字符‘a’
char a='c';
s.push_back(a); 插入字符变量a,内容是字符‘c'
char a='c';
s[0]=a; //把s[0]修改为‘c’
s[0]='b'//把s[0]修改为‘b’
}
pair
不如struct结构体
定义于< utility >,不需要特意包含。
pair<类型1,类型2>p;
类型1,类型2可以是int、double、string等,
也可以是自定义的结构体或pair<…,…>;
<>里的东西就是模板,类型1、类型2就是模板参数;
pair有两个成员变量first、second,类型分别对应类型1,类型2。
可以用make_pair(a,b)创建一个first和second分别是a、b的pair,类型自动识别。
重载了 < 运算符,优先比较first。
(是在是比较鸡肋……)
stack 栈
需要包含头文件< stack >
push(x) //把对应类型的x压入栈
top() //访问(不删除)栈顶元素
pop() //删除栈顶元素
empty() //判断栈是否空
size() //返回栈的元素数量
queue
需要包含头文件< queue >
push(x) //把x压入队列
front() //访问(不删除)队首元素
back() //访问(不删除)队尾元素
pop() //删除队首元素
empty() //判断队列是否空
size() //返回队列的元素数量
priority_queue
操作
头文件是< queue >
实际上是一个封装的堆。
priority_queue<类型>q;
需要类型重载了 < 运算符。
默认是大根堆,
可以通过取相反数来构建伪·小根堆。
或者多加两个模板参数:
priority_queue<type,vector<type>,greater<type> > Q;
注意最后两个> >之间有个空格,否则可能会被编译器认成>>。
插入和删除的复杂度都是O(logN)的。
函数
push(x) //把x插入堆
top() //访问(不删除)堆顶元素
pop() //删除堆顶元素
empty() //判断堆是否空
size() //返回堆的元素数量
(没有clear()……)
deque
操作
头文件< deque >
可以从队首和队尾进行插入和删除操作。
还可以访问队中元素。
可以用d[i]访问元素。
用于SPFA的SLF优化,不过因为常数巨大,慎重优化
函数
push_back(x)/push_front(x) //把x压入后/前端
back()/front() //访问(不删除)后/前端元素
pop_back() pop_front() //删除后/前端元素
empty() //判断deque是否空
size() //返回deque的元素数量
clear() //清空deque
支持通过sort(d.begin(),d.end())进行排序。
vector
头文件< vector >
定义:vector<类型>v;
一个不定长数组,动态倍增式申请内存
同样可以用v[i]访问第i号元素。
可以v1=v2进行赋值操作。
push_back(x) //把x压入后端
back()/front() //访问(不删除)后/前端元素
pop_back() //删除后端元素
empty() //判断vector是否空
size() //返回vector的元素数量
resize(x) //重置vector的大小为x
clear() //清空vector
支持通过sort(v.begin(),v.end())进行排序
貌似可以对已经申请了内存的地址直接修改赋值 v[i]=58;
set multiset
头文件< set >
set<类型> s;
类似于数学上的集合,因为内部的元素都是唯一的。
内部实现是红黑树!(因此需要类型重载 < 运算符)
所以插入和查询都是O(logN)的!
set常数比较大
函数
insert(x); //在集合中插入x
find(x); //寻找x,返回指向x位置的迭代器,不存在返回s.end();复杂度O(logN) 可以通过判断是否等于s.end()判断是否存在,迭代器不是很会用,所以emmm
count(x); //统计x的个数,相当于查找;由于元素是唯一的,所以只会返回0和1。
erase(x); //查找x并将其删除。复杂度O(logN)
clear(); //清空集合。
size(); //返回集合元素数
lower_bound(x)/upper_bound(x); //二分查找……
multiset
比起set只是元素可以重复而已
count函数更实用了。
erase会删掉一群……
map multimap
头文件< map >
map<类型1(key),类型2(value)>m;
类型1(key)重载 < 运算符,类型2(value)不用
类型2(value)与类型1(key)的元素一一绑定。
支持通过m[xxx]=yyy插入或访问元素,xxx是类型1,yyy是类型2。
要注意的是如果m中没有xxx这个元素那么m[xxx]会返回0(其他类型是空容器)。
听说map< xxx,bool >可以替代set……
find(x); //寻找x,返回指向x位置的迭代器,不存在返回m.end();复杂度O(logN)
count(x); //统计x的个数,相当于查找;由于元素是唯一的,所以只会返回0和1。
erase(x); //查找x并将其删除。复杂度O(logN)
clear(); //清空映射。
size(); //返回映射元素数
lower_bound(x)/upper_bound(x); //二分查找……
multimap……
key可重的映射,用法与map差不多……
那么如果重复的话m[xxx]返回的是哪一个呢?
你以为multimap还会这么良心地给你[]么……
听说这东西比multiset有用??