在ACM中库函数是非常重要的,因为有很多很多通用的操作和结构啊,非常实用,有些时候还是要深入了解一下这些库函数,码上一些库函数还有他们通用的操作。
目录
- math
- string
- algorithm
- vector
- set
- map
- queue
- stack
- 1.math
(1)int abs(int) double fabs(double)
很简单的两个函数,对int和double取绝对值,没什么好水的。
(2)double ceil(double num) double floor(double x)
这两个是很常用的取整函数,使用频率很高。ceil是向上取整,floor是向下取整。
注意这两个函数其实是带等于号的,什么意思呢,就是如果你把3传进去,他们返回的都是3。这会导致一个问题,你在计算过程中,期待的结果是一个整数,但是运算的过程中你得到了一个2.9999999,你去向下取整,你会发现你得到了一个2,这个地方经常出错,记得向下取整要加一个eps,向上同理减一个就行。
大家对浮点数构造没有了解的可以运行一下下面的代码
int main() { double f=(0.3-0.1)*10; cout<<f<<endl; printf("%.20lf\n",f); cout<<ceil(f)<<endl; cout<<floor(f)<<endl; }
我的输出结果是
2
1.99999999999999977796
2
1
这两个函数,初学者在题目中经常不加eps(我说的是我自己,大佬们开始其实都会加),提醒一下自己。
(3)double pow(double x,double y)
这个函数运算的是x^y,其实对做题基本用不到这个,我没测过复杂度是多少,但是它不能取模,用它的时候基本都是在开平方。
还有一个用法就是计算一个数字是不是平方数或者立方数之类的。
你只需要判断一下它开方后取整在平方之后是不是原来的数字就行了。这个是很快的,但是这个东西有精度限制,有时候会不对,范围大而且比较精确的还是用二分找吧。
(4)其他函数感觉没什么说的必要了,比如三角函数那一堆,学了计算几何之后没什么难点,最后说一句用math.c的时候注意精度,卡精度很常见的。
- 2.string
基本打比赛的时候没人会想用String类吧,毕竟速度太慢了,一般都用string.h.
1.其实string那些函数想说的很少,主要用的是strcpy,和strcmp。一个用来复制,一个用来判断字典序,其实只有在上课的时候用的上,字符串题应该用不到这两,有时候可能会用strcmp判断字典序什么的。
2.void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法(来自百度百科)
这个函数原型是在string.h里的,通用的操作是你可以把ch当0,或者-1,可以快速的对数组全部变成0,或者-1.
因为这个函数是按字节操作的,所以这个函数运行的常数很小,是循环赋值速度的8倍!(其实就是写起来快,写着爽!)推荐背一下,大家都在用,用了都说好!
- 3.algorithm
1.排在首位的肯定是大名鼎鼎的我啦:void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
对于这个排序大家肯定都不陌生,最简单的用法就是直接像这样:sort(a,a+n);这样就可以把a数组从小到达进行排序了。第三个是比较函数,你可以定义比较的方式的,如果缺省就是从小到大排序了。如果要从小到大排序就要自己写了。而且可以对结构体进行排序,所以对于一个经典问题如何对二元组排序,你也可以用这个函数进行计算,你只需要写一个cmp函数,说明什么时候是小于就行了(就是小于的时候返回1,大于等于返回0)。还有如果想从小到大排序的话,只需要把cmp写成,什么时候是大于就行了。还有一个对多元组排序排序的好方法,那就是pair,使用pair你可以把任意两个类型捏成一个二元组,只要这两个类型都可以进行比较就行,因为pair自己内嵌了比较的规则,是按照第一关键字为首,第二关键字为次来计算的,这个用起来也非常舒服,在二元组比较的时候可以不写结构体和比较函数,写起来很快(主要是二元组,甚至三元组这种结构简直不要太多),pair具体用法可以自行百度,介于篇幅原因,不再赘述。
最后说一点关于sort的效率问题,sort是目前最快的排序算法之一,它内部嵌套了多种排序算法会根据情况选用,基本不可能出现比这种排序还快的方案,你完全可以相信它nlogn的复杂度。
2.lower_bound()和upper_bound()
这两个函数很多情况下会用的上,因为你要在一个有序的序列中查询一个数的位置,这种操作十分常见
一般的用法是lower_bound(a,a+n,x)或者lower_bound(a,a+n,x,cmp)
分别是开始地址,结束地址,要查询的数值,比较函数。第四个比较函数是可以缺省的,默认是找大于等于x的第一个位置,返回值不是下标!是它所在的地址。所以你要获得数组下标,你还要剪掉开始地址才能得到下标。
注意在使用这两个函数时一定要保证查询的数组时有序的(一般是从小到大)
lower_bound()返回的是第一个大于等于x的位置地址,upper_bound()返回的是第一个大于等于x的位置地址。如果不存在,则会返回结束地址。
但是你这时候就会想了,我想查询小于等于x的位置怎么办呢,其实你可以把原来的数组从大到小排序,然后自己写一下cmp函数(大于形式的)。但是我一直觉得这种比较麻烦,你可以把所有的数字都取负号,然后查询的时侯查询-x的位置就行了,这样写起来很快。
3.还有一些小的函数特别有用但是没啥难度的:
max min swap
除了这些我就想不起来什么常用了
- 4.vector
1.先谈谈vector的机制吧
vector对于小白来说可以理解成一个变长数组,但是我十分不推荐这种理解,因为我当初学习的时候有人就是这么说的,让我了解到了一个可以新增元素,但是却可以o(1)时间查询的奇怪玩意。但是vector不是一个会变长度的数组,它其实是一个已经开好了空间的数组,你每次往这个向量中添加一个元素的时候其实就是和数组操作没什么区别。这时候你就想了,vector要开多大的空间呢?其实它开空间的时候是动态开的,或许它刚开始只有100的大小,但是这100个要是全部都被装满了,它就会开一个新的连续的200的空间,然后把这100复制到200的那里,然后把100的释放掉。如果200的再满了,就开一个400的,以此类推。所以vector其实有时候是比较慢的(应该没人卡这个吧),但是也无伤大雅,基本是够用的,但是vector的一些函数尽量少用,有些操作的o(n)的,这个就是一个数组,没什么特别的。
2.vector<int> a;
这是一个简洁的初始化,其实对于vector的初始化太多了,而且没什么讲的,掠过。
3.压入push_back(),清空clear()
这两个操作可以说是最常见的操作了,在建图的过程中经常会用的到。
4.vetor和sort
sort(a.begin(),a.end());
非常暴力,但是非常好用。
5.vector内嵌了好多神奇操作,我曾见过cf大神为了追求速度,只用vector当数组用的,vector的函数有非常多种,都记住可以大量提高效率(但是觉得麻烦一直用的数组,看着舒服一点,想提高自己的速度的可以练一练,感觉是都可以被替代的操作,不会也没关系)
- 5.set
1.set的原理:其实就是一种平衡树,你们以后都会学习的,可以把查找的结构写的非常的好,复杂度查询和插入都是logn的,很稳定,但是开空间的常数也不小就是了,要是卡时间的话能hash尽量hash。注意:set只是判断有没有,不能判断出现了几次。
2.insert erase
insert(key_value),插入一个键值key_value
erase(iterator) ,删除iterator指向的值
erase(first,second),删除定位器first和second之间的值
erase(key_value),删除键值key_value的值
3.count(key_value)
可以看这个值出现了多少次,但是因为是集合,如果有返回0,否则返回1
4.find(key_value)
返回的是迭代器,如果不存在就返回end()
5.迭代器iterator
迭代器是STL中都有的一种类似数组下标一样的东西,迭代器可以++,但是不能一次跳跃多次。
所以说set是不能查询第k大的,因为树形结构不能让迭代器一次跳跃很多次。
迭代器格式:set<int>::iterator it;
for(it=s.begin ();it!=s.end ();it++) 操作;
迭代器是一种指针,要*it才能拿到它所指向的值。
6.lower_bound()和 upper_bound()
虽然set不支持查询第k大,但是我们可以查询比一个数x大于(等于)的第一个迭代器是什么,也是很不错了。
7.还有一些其他的小技巧,比如size(),empty()什么的,就没什么可说的了,用过一次就记住了。
- 6.map
1.map原理:其实和set差不多,也是一颗红黑树,不过多了另外一个键值value,其他的操作和set基本一样的。多了一个功能就是可以查询键值对应的值。在set中,存的是一个键值,而在map中存的是一个pair结构,第一个是键值,只能出现一次,第二个是值。
2.map用来干什么?
map是一个映射工具,它可以用logn的时间去查询一个键值对应的值,你可以用它去计数,或者就当一个logn的hash来用,但是一般卡时间的话用unordered_map,如果还不行就手写一个hash,应该就可以过了。主要是map写起来真的好方便,你只要把你要查询的值用中括号扩上就可以啦,就可以返回你要的啦,和数组一样,真的好好用啊。
3.对于map的操作基本和set差不多,网上写的好的博客很多了,就不再赘述了。
- 7.queue
1.queue是一种只可以先进先出的数据结构,因为这种结构在很多算法中都有涉及,非常常用,所以也作为常用库之一,虽然其实现并不复杂,但是非常实用。
2.最常用的入队,出队,获取队头,获取队尾,大小,是否为空,都很容易,就不再赘述。
3.优先队列:priority_queue<Type, Container, Functional>,其中Type 为数据类型,Container为保存数据的容器,Functional 为元素比较方式。
一般的用法是不带后两个的:
priority_queue<int> q;
这个队列其实是个最大堆的实现,出队的时候是用的是top,不是front。pop的时候也是会出队最大的那一个。
注意一下,这个默认的是最大堆,要是最小堆的话,就要加上后面两个参数:
priority_queue<int,vector<int>,greater<int> > q;
如果不是int就把int换了就行。
比如单调队列就使用了队列,还有djstl使用过了优先队列
- 8.stack
1.栈其实和队列特别像,很多操作都是共有的,对于栈,一个先进去的数,一定后更后面出来,和队列相反,像是一个桶。
2.常用函数不再赘述,网上很多的。
3.用途:对于编译器来说,你运行的函数就相当于一个栈。很多模拟的结构和栈也特别的像。单调栈。
写到这里我的手已经要被敲断了,很多地方没办法就省下去,毕竟网上很多博客写的都是特别的全。
poj 2823
原文地址:https://www.cnblogs.com/King-of-Dark/p/12030796.html