Day6讲了三个大部分的内容。
1.STL
2.初等数论
3.倍增
Part1主要与STL有关。
1.概述
STL的英文全名叫Standard Template Library,翻译成中文就叫标准模板库。
它有点类似于一个大型的工具箱,里面包含许多实用工具,可以拿过来直接用而大部分情况下无需去深入探究其内部原理。
不知道从什么时候开始,CCF不再限制选手使用STL,所以在OI赛事中STL被广泛应用。
它分为六个大部分:
1)容器 containers
2)迭代器 iterators
3)空间配置器 allocateor
4)配接器 adapters
5)算法 algorithms
6)仿函数 functors
在OI中应用非常广泛的是容器和算法,迭代器有可能会用到,另外三个不常见。
本篇随笔,我会简单谈一下迭代器,算法和容器方面的一些浅层知识。
我在学习STL的时候是基于实用主义的,之前我也谈到了,不需要过分的探求它的内部原理,只需要知道如何使用即可。
2.迭代器
迭代器是一种检查容器内元素并遍历元素的数据类型。它如同一个指针。
注意:C++当中的指针可以认为是一种迭代器,而迭代器不全是指针。
迭代器分为以下几种:
- Input iterators 提供对数据的只读访问
- Output iterators 提供对数据的只写访问
- Forward iterators 提供读写操作,并可以向前驱动迭代器
- Bidirectional iterators 提供读写操作,并可以双向(前,后)驱动迭代器
- Random access iterators 提供读写操作,可以在数据中随机的移动
至于具体的迭代器实例,我会在后面提及。
3.算法
要想使用STL的算法,必须先加一个#include<algorithm>
顾名思义,你可以用这里面的函数完成很多操作。我们在之前的程序中也用到了STL库的算法,它会对我们编写程序带来极大的方便(只要你合理使用)。
3.1排序
(自从我会用这个进行排序之后,我就再也没写过人工快排,现在让我写人工快排我也不一定能写出来。。。)
STL提供的排序算法有很多,最常用也是最常见的是std::sort(RandomAccessIterator first,RandomAccessIterator last),它的时间复杂度是O(nlogn),没有返回值,内部实现类似快速排序,是一种不稳定的排序算法。
(正在看这篇随笔的读者朋友,我相信您知道什么是不稳定的排序算法)
还有一种稳定的排序算法,叫std::stable_sort(RandomAccessIterator first,RandomAccessIterator last),内部实现为归并排序,时间复杂度O(nlogn),没有返回值,不过我用的很少。
从参数名可以看出,这个函数出的参数是两个迭代器,同时可以理解为指向要排序的数组的指针。其中RandomAccessIterator first代表数组的首地址,RandomAccessIterator last代表数组的尾地址。通常的,数组名可以作为数组的首地址的指针,而数组的尾地址通常使用首地址+偏移量来代替。
(偏移量:相对于数组首地址偏移的长度,在这里应是数组长度)
应用:sort(a,a+n); 或 sort(a+1,a+n+1);
假设我们要排序的数组为a,则前者排序范围是a[0] ~ a[n-1],后者排序范围是a[1] ~ a[n]。
这两种方式按照实际情况选择使用。
3.1.1自定义比较器与基于struct(或class)的多关键字排序
一般的,STL的排序算法默认是按照整形的从小到大排序,但有的时候我们需要从大到小排序或者要排序的对象是一个自己写的struct(或 class),这样STL的排序算法就不太适合我们的需求了,我们应该稍加改进。
STL有一个默认的比较器,就是上文所述的按照整形从小到大。同时,STL支持传入一个自定义比较器(一般是函数)来作为一个新的比较规则,然后它可以按照新的比较规则进行排序。
这样的写法应该就是std::sort(RandomAccessIterator first,RandomAccessIterator last,Compare comp);
传入的comp应该自己实现,它应该是一个给定的struct或class的实例或者是一个返回值为bool或int的函数。
- bool comp(int a,int b){
- return a > b;
- }
只需要这样写,就能让排序算法按照从小到大排序。
struct的多关键字排序:
- strruct my_struct{
- int x,y;
- };
- my_struct a[maxn];
- bool comp(const my_struct &a,const my_struct &b){
- return (a.x < b.x || (a.x==b.x && a.y < b.y));
- }
- sort(a,a+maxn,comp);
这样就可以让排序函数按照x值为第一关键字,y值为第二关键字进行从小到大的排序。
3.1.2 多关键字排序的运算符重载方法
对于多关键字排序,还可以使用运算符重载。
运算符重载类似于重新定义一个符号,而在排序时函数优先使用这个被重定义的比较规则。
- struct my_struct{
- int x,y;
- bool operator<(const my_struct &r)const{
- return (x<r.x || (x==r.x && y<r.y));
- }
- }
- my_struct a[maxn];
- sort(a,a+n);
3.2 min和max
std::min & std::max
返回二者中的较大值/较小值。
3.3 max_element & min_element
返回一段连续数组(或容器)中的一个最大值或最小值的指针(迭代器)
时间复杂度O(n),但我更喜欢手写。。
3.4lower_bound & upper_bound
在一个升序数组上进行二分查找可以用这两个函数。
std::lower_bound 返回第一个第一个大于等于查询值位置的迭代器
std::upper_bound 返回第一个大于等于查询值位置的迭代器
时间复杂度O(logn)。
示例程序:
- #include<iostream>
- #include<algorithm>
- using namespace std;
- int main(){
- int a[],n;
- cin >> n;
- for (int i=;i<=n;i++)
- cin >> a[i];
- int *first_bigger_or_equal_iterator = lower_bound(a+,a+n+,);
- cout << first_bigger_or_equal_iterator - a << endl;
- return ;
- }
示例输入:5 2 3 5 7 8
示例输出:3
(如果是upper_bound,输出应该是4)
3.5 swap
交换两个对象,是的,什么类型都可以换,比自己写的swap不知道高到哪里去了。
template <class T>
void swap(T& a,T& b);
3.6去重
ForwardIterator unique(ForwardIterator first ,ForwardIterator last);
删掉相邻连续一段中的重复元素,返回一个指向去重后的最后一个位置的下一个位置的迭代器,时间复杂度O(n)。
经常被用在离散化操作上。
3.7排列
bool next_permutation(BidirectionalIterator first,BidirectionIterator last);
这个函数按照字典序生成当前段的下一个排列,如果生成成功则返回 true,否则返回false,时间复杂度O(n)。
bool prev_permutation(BidirectionalIterator first,BidirectionIterator last);
同上,只不过它生成上一个排列。
常用来枚举全排列,用它替代手写DFS
(当然手写全排列DFS不难写,所以有很多人仍喜欢用手写DFS而不是这个东西)
4.容器
容器就是各种封装好的数据结构,它分为三大类,序列容器,关联容器和其他容器。
同时容器还配备了许多实用的方法与函数,只要include了相应的头文件,我们就可以拿过来直接用。
4.1栈
序列容器。
STL栈,可以实现栈所有的基本功能。
需要#include<stack>
声明一个栈:stack<基类型,一般是int> s;
压入:s.push(x);
弹出:s.pop();
取栈顶但不弹出:s.top();
返回栈大小(元素个数):s.size();
判断栈空:s.empty();
4.2队列
序列容器。
需要#include<queue>
声明与基本操作和STL栈类似,把stack换成queue即可。
注意取队首操作,应该是q.front(),而不是q.top()(这是优先队列的取队首写法)。
4.3双端队列
序列容器。
声明使用 deque,允许在两端进行插入 删除操作,但是性能很差,不推荐使用,知道有这么个东西就好。
需要#include<deque>
常用操作:
入队首 push_front
弹出队首 pop_front
访问队首 front
入队尾 push_back
弹出队尾 pop_back
访问队尾 back
4.4 向量(不定长大数组)
序列容器。
需要#include<vector>
它的本质是一个不定长大数组,其占用空间的大小是动态的,申请空间操作是动态的。
当不知道要开多大的数组时,用vector可以节省空间。
vector中的数据在内存中占据的空间是连续的一段,每次不够时,就在别的地方再开两倍大内存空间,并将原来的数据进行转移。
但它的操作是非常慢的,不推荐使用。
4.5 对组
关联容器。
需要#include<uillity>
std::pair把两个数据类型打包成一个队组,用first和second访问值。
用途:代替只有两个成员变量的struct(说成偷懒也不足为过)
可以用来方便的表示一个二维平面的点,或者是一个区间。
有的人喜欢在写dijsktra的时候用vector和这个东西,我的代码风格不是这个流派的,所以不贴了。
4.6优先队列
序列容器。
要是不对时间过分苛求,这个东西就是个大神器,大神器,大神器。
需要#include<queue>,声明用priority_queue。
它名叫优先队列,却满足堆的所有性质,所以也能把这个当一个大根堆用(默认把比较大的数放在队列前面)。
要把这个东西和单调队列区分开,它们内部存储方式不太一样。
插入和删除的复杂度是O(logn),比较优秀。在绝大多数情况下可以代替手写堆。
要把它变成小根堆,一般俩方法。
1.这样写:priority_queue<int,vector<int>,greater<int> > q;
(注意后面那两个"<"要分开,要不编辑器会认为是流运算符"<<")
2.运算符重载,我没用过这个方法。。。
操作方式和一般容器类似。
4.7集合
关联容器。
需要#include<set>
std::set,内部用平衡树实现,保持内部元素有序,插入重复元素会自动忽略。
可以在O(logn)的时间内完成元素的插入,删除,查找等操作。
特色操作:
插入 insert
删除 erase
查找 find
查找个数(是否存在)count
二分查找 lower_bound,upper_bound
查询大小 size
判空 empty
返回第一个元素的迭代器 begin
返回最后一个元素的迭代器 end
清空(初始化)clear
还有一个multiset,与set类似,但可以存储相同的元素。
4.8映射
关联容器。
又是一个大神器。
需要#include<map>,其内部也用平衡树实现,保持内部元素的有序,同样的可以在O(logn)的时间内完成元素的插入,删除,查找等操作。
它内部的元素和“下标”之间有一个一一对应的关系,也就是键与值的关系,这个关系是不能重复的。每一个所谓的下标和键值由一个pair存储。
有时也用于离散化。
4.9字符串
序列容器。
挺好用,但用的不多。
需要#include<string>,存储的是一个变长字符串,每个字符都是char类型,支持下标访问,本质类似于vector<char>.
特色操作
取子串 substr
支持运算符"+"拼接两个string类,首尾连接。
支持运算符==,<等判断两个string类的大小关系。
4.10 bitset
其他容器。
用来管理一些二进制位,可以访问指定下标的bit,还能把它们作为一个整数进行统计。
NOIP难度可能用不到这个。
5.STL的局限性
STL虽然好用,但是在没有优化的情况下运行速度通常比等价的手写代码运行要慢,所以在一些题目当中会出现卡STL的现象。
(所以STL也叫Sometimes TLE Library
日常刷题用一下还是可以的,比赛时是否要用STL,请酌情选择。
- 夏令营讲课内容整理 Day 7.
Day7是夏令营的最后一天,这一天主要讲了骗分技巧和往年经典的一些NOIP试题以及比赛策略. 这天有个小插曲,上午的day7T3是一道和树有关的题,我是想破脑袋也想不出来,正解写不出来就写暴力吧,暴力 ...
- 夏令营讲课内容整理 Day 3.
本日主要内容是树与图. 1.树 树的性质 树的遍历 树的LCA 树上前缀和 树的基本性质: 对于一棵有n个节点的树,必定有n-1条边.任意两个点之间的路径是唯一确定的. 回到题目上,如果题 ...
- 夏令营讲课内容整理Day 0.
今年没有发纸质讲义是最气的.还好我留了点课件. 第一次用这个估计也不怎么会用,但尝试一下新事物总是好的. 前四天gty哥哥讲的内容和去年差不多,后三天zhn大佬讲的内容有点难,努力去理解吧. 毕竟知识 ...
- 夏令营讲课内容整理 Day 6 Part 3.
第三部分主要讲的是倍增思想及其应用. 在Day3的整理中,我简要提到了倍增思想,我们来回顾一下. 倍增是根据已经得到的信息,将考虑的范围扩大一倍,从而加速操作的一种思想,它在变化规则相同的情况下,加速 ...
- 夏令营讲课内容整理 Day 6 Part 2.
Day 6的第二部分,数论 数论是纯粹数学的分支之一,主要研究整数的性质 1.一些符号: a mod b 代表a除以b得到的余数 a|b a是b的约数 floor(x) 代表x的下取整,即小于等于 ...
- 夏令营讲课内容整理 Day 5.
DP专场.. 动态规划是运筹学的一个分支, 求解决策过程最优化的数学方法. 我们一般把动态规划简称为DP(Dynamic Programming) 1.动态规划的背包问题 有一个容量为m的背包,有 ...
- 夏令营讲课内容整理 Day 4.
本日主要内容就是搜索(打暴力 搜索可以说是OIer必会的算法,同时也是OI系列赛事常考的算法之一. 有很多的题目都可以通过暴力搜索拿到部分分,而在暴力搜索的基础上再加一些剪枝优化, 就有可能会拿到更多 ...
- 夏令营讲课内容整理 Day 2.
本日主要内容是并查集和堆. 并查集 并查集是一种树型的数据结构,通常用来处理不同集合间的元素之间的合并与查找问题.一个并查集支持三个基本功能:合并.查找和判断.举一个通俗的例子,我和lhz认识,lhz ...
- 夏令营讲课内容整理Day 1.
主要内容是栈和队列. 1. 栈 运算受到限制的线性表.只允许从一端进行插入和删除等操作.这一端便是栈顶,另一端便是栈底. 其实可以把栈想象层任何有底无盖的柱状的容器...毕竟栈满足后进先出的特性.计 ...
随机推荐
- c++(堆排序)
堆排序是另外一种常用的递归排序.因为堆排序有着优秀的排序性能,所以在软件设计中也经常使用.堆排序有着属于自己的特殊性质,和二叉平衡树基本是一致的.打一个比方说,处于大堆中的每一个数据都必须满足这样一个 ...
- CentOS 6.5 编译安装 LNMP环境
建立一个软件包目录存放 mkdir -p /usr/local/src/ 清理已经安装包 rpm -e httpd rpm -e mysql rpm -e php yum -y remove http ...
- [国嵌笔记][026][ARM伪指令]
ARM机器码 1.汇编程序通过汇编器变成机器码,然后才能在ARM处理器上运行 2.ARM机器码是一个32位的数,被分成了多个段,每个段都有各自的含义 3.格式: cond:表示条件(4位) I:表示源 ...
- ThinkPHP3.2 实现Mysql数据库备份
<?php header("Content-type:text/html;charset=utf-8"); //配置信息 $cfg_dbhost = 'localhost'; ...
- 修改DeDe标签Pagelist分页样式,自定义分页样式
我们在用dede仿站的时候,调用文章列表页的分页时,我们会用到: {dede:pagelist listitem="info,index,end,pre,next,pageno" ...
- ZooKeeper集群的安装、配置、高可用测试
Dubbo注册中心集群Zookeeper-3.4.6 Dubbo建议使用Zookeeper作为服务的注册中心. Zookeeper集群中只要有过半的节点是正常的情况下,那么整个集群对外就是可用的.正是 ...
- vue学习笔记(一)——why Vue
- xss防御
http://blog.csdn.net/ghsau/article/details/17027893
- 【笔记】vue-cli 开发环境中跨域连接后台api(vue-resource 跨域post 请求)
在vue-cli 项目中很多人会用到mock 数据(模拟数据),但是我觉得如果在真实的数据库交互中开发会更有安全感一些,所以查了一下百度很多人推荐的就是: 跨域! 跨域是什么概念?不同的主机名,同主机 ...
- 第一个RESTful API
一个简单的测试 /** * Created by M.C on 2017/9/8. */ var superagent = require('superagent'); var expect = re ...