夏令营讲课内容整理 Day 6 Part 1.

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)。

示例程序:

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 int main(){
 5 int a[100],n;
 6 cin >> n;
 7     for (int i=1;i<=n;i++)
 8     cin >> a[i];
 9     int *first_bigger_or_equal_iterator = lower_bound(a+1,a+n+1,5);
10     cout << first_bigger_or_equal_iterator - a << endl;
11     return 0;
12 }

示例输入: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,请酌情选择。

时间: 2024-08-09 10:44:25

夏令营讲课内容整理 Day 6 Part 1.的相关文章

夏令营讲课内容整理Day 0.

今年没有发纸质讲义是最气的.还好我留了点课件. 第一次用这个估计也不怎么会用,但尝试一下新事物总是好的. 前四天gty哥哥讲的内容和去年差不多,后三天zhn大佬讲的内容有点难,努力去理解吧. 毕竟知识还是需要消化的. 这里我只整理知识点,每天上午评测的题目我会单独处理. 嗯大概就是这样了. 写完后我就会考虑发到博客园里.

夏令营讲课内容整理 Day 3.

本日主要内容是树与图. 1.树 树的性质 树的遍历 树的LCA 树上前缀和 树的基本性质: 对于一棵有n个节点的树,必定有n-1条边.任意两个点之间的路径是唯一确定的. 回到题目上,如果题目读入的是树上所有的边,则我们应该想到: 每个点的父亲是谁 每个点的深度 每个点距离根节点的距离 其他的附加信息(例如:子树和,子树最大值..) 遍历整个树的代码如下: 1 void dfs(int now) 2 { 3 deep[now]=deep[fa[now]]+1; 4 sum[now]=value[n

夏令营讲课内容整理 Day 5.

DP专场.. 动态规划是运筹学的一个分支, 求解决策过程最优化的数学方法. 我们一般把动态规划简称为DP(Dynamic Programming) 1.动态规划的背包问题 有一个容量为m的背包,有n个物品,每一个物品i的重量为w[i],价值为v[i]. 要求选择一些物品放入背包中,每种物品只能最多使用一次,使得在不超重的情况下让背包中所有物品价值总和最大. 正常向解法:设状态数组f[i][j]为把前i个物品放入一个容量为j的背包中所能获得的最大价值(以下同设),则状态转移方程为: f[i][j]

夏令营讲课内容整理 Day 4.

本日主要内容就是搜索(打暴力 搜索可以说是OIer必会的算法,同时也是OI系列赛事常考的算法之一. 有很多的题目都可以通过暴力搜索拿到部分分,而在暴力搜索的基础上再加一些剪枝优化, 就有可能会拿到更多的分数. 有句话说的好嘛,骗分过样例,暴力出奇迹. 真的可以出奇迹的,只要你用得好. 1.搜索的概念 在一个给定的空间内,运用一定的查找(遍历)方式,直到找到目标解(状态)的过程,我们称之为搜索. 搜素是尝试性的,搜索是无脑的,搜索是朴素的,搜索在很多时候是显然的,搜索应该总是暴力的.但搜索也是很常

夏令营讲课内容整理Day 1.

主要内容是栈和队列. 1.  栈 运算受到限制的线性表.只允许从一端进行插入和删除等操作.这一端便是栈顶,另一端便是栈底. 其实可以把栈想象层任何有底无盖的柱状的容器...毕竟栈满足后进先出的特性.计算机当中调用函数时,中间结果便会保存到「系统栈」中.递归过程也需要栈的协助 . 实现:STL or 手写(请参照一本通 or 课件) 一般操作:判断栈空/满.入栈.出栈,判断栈的大小(请参照一本通 or 课件) 1.1 单调栈 顾名思义,保证内部元素单调(单增或单减)的栈.我们只要在插入新元素的时候

夏令营讲课内容整理 Day 6 Part 2.

Day 6的第二部分,数论 数论是纯粹数学的分支之一,主要研究整数的性质 1.一些符号: a mod b 代表a除以b得到的余数 a|b a是b的约数 floor(x) 代表x的下取整,即小于等于x的最大整数,也可以认为是直接舍去小数部分 (这个应该是一个符号,但我不知道怎么打出来..下面那个ceil也是) ceil(x) 代表x的上取整,即大于等于x的最小整数,也可以认为是直接舍去小数部分再+1. gcd(a,b) 表示a与b的最大公约数 lcm(a,b) 表示a与b的最小公倍数 累加符号∑

夏令营讲课内容整理 Day 2.

本日主要内容是并查集和堆. 并查集 并查集是一种树型的数据结构,通常用来处理不同集合间的元素之间的合并与查找问题.一个并查集支持三个基本功能:合并.查找和判断.举一个通俗的例子,我和lhz认识,lhz和hzc认识,那么也就可以断定我和hzc认识. 依照并查集的思想,我们把所有要待处理的元素a1,a2,a3....an这n个元素都看作是一个单独的集合,初始状态每个集合都只有一个元素.我们就可以把并查集的合并操作理解为集合之间的取并集操作. 作为一个树形结构,在一个由许多这样的集合构成的森林中,每个

Google C++ 风格指南内容整理

之前一直没有全面的看过Google C++风格指南,现在很多公司进行C++开发都要求按照Google C++风格.在这个网站 http://zh-google-styleguide.readthedocs.org/en/latest/contents/  有人已经把其翻译成中文.为了便于以后查看,下面的内容完全是来自于这个网站,只是把多个网页的内容整理放在了一起. 1.      头文件: 通常每一个.cc文件都有一个对应的.h文件.也有一些常见例外,如单元测试代码和只包含main()函数的.c

网页格式化排版代码,专用信息采集后的内容整理

public static string ClearHtml(string content) { Regex regex = new Regex(""); //首先把p标签的属性去掉,只留<p> regex = new Regex(@"<p.*?>", RegexOptions.IgnoreCase | RegexOptions.Singleline); content = regex.Replace(content, "<p