【ZJSU - 大红大紫:ACM - Template】比赛用模板12:STL与库函数
作者:互联网
\(\tt STL\) 与库函数
后继 \(\tt lower\_bound、upper\_bound\)
lower
表示 \(\ge\) ,upper
表示 \(>\) 。使用前记得先进行排序。
//返回a数组[start,end)区间中第一个>=x的地址【地址!!!】
cout << lower_bound(a + start, a + end, x);
cout << lower_bound(a, a + n, x) - a; //在a数组中查找第一个>=x的元素下标
upper_bound(a, a + n, k) - lower_bound(a, a + n, k) //查找k在a中出现了几次
二分搜索 \(\tt binary\_search\)
用于查找某一元素是否在容器中,相当于 find 函数。在使用前需要先进行排序。
//在a数组[start,end)区间中查找x是否存在,返回bool型
cout << binary_search(a + start, a + end, x);
批量递增赋值函数 \(\tt{}iota\)
对容器递增初始化。
//将a数组[start,end)区间复制成“x,x+1,x+2,…”
iota(a + start, a + end, x);
数组去重函数 \(\tt{}unique\)
在使用前需要先进行排序。
其作用是,对于区间 [开始位置, 结束位置)
,不停的把后面不重复的元素移到前面来,也可以说是用不重复的元素占领重复元素的位置。并且返回去重后容器中不重复序列的最后一个元素的下一个元素。所以在进行操作后,数组、容器的大小并没有发生改变。
//将a数组[start,end)区间去重,返回迭代器
unique(a + start, a + end);
//与earse函数结合,达到去重+删除的目的
a.erase(unique(ALL(a)), a.end());
位运算函数 \(\tt{}\_\_builtin\_\)
//样例:x = 1即(1),x = 8即(1000),x = 15即(1111)
//返回x二进制下含1的数量
cout << __builtin_popcount(x); //例如x=15时答案为4
//返回x二进制下最后一个1的位置(从1开始计算)
cout << __builtin_ffs(x); //例如x=1答案为1,x=8答案为4
//返回x二进制下后导0的个数
cout << __builtin_ctzll(x); //例如x=1答案为0,x=8答案为3
注:以上函数的 \(\tt{}long\ long\) 版本只需要在函数后面加上 ll
即可(例如 __builtin_popcountll(x)
, \(\tt{}unsigned\ long\ long\) 加上 ull
。
全排列算法 \(\tt next\_permutation、prev\_permutation\)
在提及这个函数时,我们先需要补充几点字典序相关的知识。
对于三个字符所组成的序列
{a,b,c}
,其按照字典序的6种排列分别为:
{abc}
,{acb}
,{bac}
,{bca}
,{cab}
,{cba}
其排序原理是:先固定a
(序列内最小元素),再对之后的元素排列。而b
<c
,所以abc
<acb
。同理,先固定b
(序列内次小元素),再对之后的元素排列。即可得出以上序列。
\(\tt{}next\_permutation\) 算法,即是按照字典序顺序输出的全排列;相对应的, \(\tt{}prev\_permutation\) 则是按照逆字典序顺序输出的全排列。可以是数字,亦可以是其他类型元素。其直接在序列上进行更新,故直接输出序列即可。
使用方法1:输出一组数据的全排列
int a[4] = {4, 1, 3, 2};
sort(a, a + 4);
do {
for (int i = 0; i < 4; ++ i) cout << a[i] << " ";
cout << endl;
} while (next_permutation(a, a + 4));
使用方法2:输出一组数据的第 \(k\) 个排列
int a[7] = {1, 2, 3, 4, 5, 6, 7}, num = 0;
sort(a, a + 7);
int k; cin >> k;
do {
if (num == k) {
for (int i = 0; i < 7; ++ i) cout << a[i] << " ";
cout << endl;
break;
}
++ num;
} while (next_permutation(a, a + 7));
字符串转换为数值函数 \(\tt{}sto\)
可以快捷的将一串字符串转换为指定进制的数字。
使用方法
stoi(字符串, 0, x进制)
:将一串 \(\tt{}x\) 进制的字符串转换为 \(\tt{}int\) 型数字。
stoll(字符串, 0, x进制)
:将一串 \(\tt{}x\) 进制的字符串转换为 \(\tt{}long\ long\) 型数字。- \(\tt{}stoull,stod,stold\) 同理。
数值转换为字符串函数 \(\tt to\_string\)
允许将各种数值类型转换为字符串类型。
//将数值num转换为字符串s
string s = to_string(num);
判断非递减 \(\tt is\_sorted\)
//a数组[start,end)区间是否是非递减的,返回bool型
cout << is_sorted(a + start, a + end);
累加 \(\tt accumulate\)
//将a数组[start,end)区间的元素进行累加,并输出累加和+x的值
cout << accumulate(a + start, a + end, x);
迭代器 $\tt iterator $
//构建一个UUU容器的正向迭代器,名字叫it
UUU::iterator it;
vector<int>::iterator it; //创建一个正向迭代器,++ 操作时指向下一个
vector<int>::reverse_iterator it; //创建一个反向迭代器,++ 操作时指向上一个
元组 \(\tt tuple\)
//获取obj对象中的第index个元素——get<index>(obj)
//需要注意的是这里的index只能手动输入,使用for循环这样的自动输入是不可以的
tuple<string, int, int> Student = {"Wida", 23, 45000);
cout << get<0>(Student) << endl; //获取Student对象中的第一个元素,这里的输出结果应为“Wida”
栈 \(\tt stack\)
栈顶入,栈顶出。先进后出。
//没有clear函数
size() / empty()
push(x) //向栈顶插入x
top() //获取栈顶元素
pop() //弹出栈顶元素
队列 \(\tt queue\)
队尾进,队头出。先进先出。
//没有clear函数
size() / empty()
push(x) //向队尾插入x
front() / back() //获取队头、队尾元素
pop() //弹出队头元素
//没有clear函数,但是可以用重新构造替代
queue<int> q;
q = queue<int>();
双向队列 \(\tt deque\)
size() / empty() / clear()
push_front(x) / push_back(x)
pop_front(x) / pop_back(x)
front() / back()
begin() / end()
[]
优先队列 \(\tt priority\_queue\)
默认升序(大根堆),自定义排序需要重载 <
。
//没有clear函数
priority_queue<int, vector<int>, greater<int> > p; //重定义为降序(小根堆)
push(x); //向栈顶插入x
top(); //获取栈顶元素
pop(); //弹出栈顶元素
//重载运算符【注意,符号相反!!!】
struct Node {
int x; string s;
friend bool operator < (const Node &a, const Node &b) {
if (a.x != b.x) return a.x > b.x;
return a.s > b.s;
}
};
字符串 \(\tt string\)
size() / empty() / clear()
//从字符串S的S[start]开始,取出长度为len的子串——S.substr(start, len)
//len省略时默认取到结尾,超过字符串长度时也默认取到结尾
cout << S.substr(1, 12);
有序、多重有序集合 \(\tt set\) 、\(\tt multiset\)
默认升序(大根堆),\(\tt set\) 去重,\(\tt multiset\) 不去重,\(\mathcal O(logN)\) 。
set<int, greater<> > s; //重定义为降序(小根堆)
size() / empty() / clear()
begin() / end()
++ / -- //返回前驱、后继
insert(x); //插入x
find(x) / rfind(x); //顺序、逆序查找x,返回迭代器【迭代器!!!】,没找到时返回end()
count(x); //返回x的个数
lower_cound(x); //返回第一个>=x的迭代器【迭代器!!!】
upper_cound(x); //返回第一个>x的迭代器【迭代器!!!】
erase(x);
有两种删除方式:
- 当x为某一元素时,删除所有这个数,复杂度为 \(\mathcal O (num_x+logN)\) ;
- 当x为迭代器时,删除这个迭代器。
//连续头部删除
set<int> S = {0, 9, 98, 1087, 894, 34, 756};
auto it = S.begin();
int len = S.size();
for (int i = 0; i < len; ++ i) {
if (*it >= 500) continue;
it = S.erase(it); //删除所有小于500的元素
}
//错误用法如下【千万不能这样用!!!】
//for (auto it : S) {
// if (it >= 500) continue;
// S.erase(it); //删除所有小于500的元素
//}
\(\tt map\) 、\(\tt multimap\)
默认升序(大根堆),\(\tt map\) 去重,\(\tt mulitmap\) 不去重,\(\mathcal O(logS)\) ,其中 \(S\) 为元素数量。
map<int, int, greater<> > mp; //重定义为降序(小根堆)
size() / empty() / clear()
begin() / end()
++ / -- //返回前驱、后继
insert({x, y}); //插入二元组
[] //随机访问,multimap不支持
count(x); //返回x为下标的个数
lower_cound(x); //返回第一个下标>=x的迭代器
upper_cound(x); //返回第一个下标>x的迭代器
erase(x);
有两种删除方式:
- 当x为某一元素时,删除所有以这个元素为下标的二元组,复杂度为 \(\mathcal O (num_x+logN)\) ;
- 当x为迭代器时,删除这个迭代器。
慎用随机访问!——当不确定某次查询是否存在于容器中时,不要直接使用下标查询,而是先使用 count()
或者 find()
方法检查key值,防止不必要的零值二元组被构造。
int q = 0;
if (mp.count(i)) q = mp[i];
慎用自带的 \(\pmb{\tt pair、tuple}\) 作为key值类型!使用自定义结构体!
struct fff {
LL x, y;
friend bool operator < (const fff &a, const fff &b) {
if (a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
};
map<fff, int> mp;
\(\tt bitset\)
将数据转换为二进制,从高位到低位排序,以 \(0\) 为最低位。当位数相同时支持全部的位运算。
//使用只含01的字符串构造——bitset<容器长度>B (字符串)
string S; cin >> S;
bitset<32> B (S);
//使用整数构造(两种方式)
int x; cin >> x;
bitset<32> B1 (x);
bitset<32> B2 = x;
[] //随机访问
set(x) //将第x位置1,x省略时默认全部位置1
reset(x) //将第x位置0,x省略时默认全部位置0
flip(x) //将第x位取反,x省略时默认全部位取反
to_ullong() //重转换为ULL类型
to_string() //重转换为ULL类型
count() //返回1的个数
any() //判断是否至少有一个1
none() //判断是否全为0
bitset<23> B1("11101001"), B2("11101000");
cout << (B1 ^ B2) << "\n"; //按位异或
cout << (B1 | B2) << "\n"; //按位或
cout << (B1 & B2) << "\n"; //按位与
cout << (B1 == B2) << "\n"; //比较是否相等
哈希系列 \(\tt unordered\)
通常指代 \(\tt unordered\_map、unordered\_set、unordered\_multimap、unordered\_multiset\) ,与原版相比不进行排序。
如果将不支持哈希的类型作为 key
值代入,编译器就无法正常运行,这时需要我们为其手写哈希函数。而我们写的这个哈希函数的正确性其实并不是特别重要(但是不可以没有),当发生冲突时编译器会调用 key
的 operator ==
函数进行进一步判断。参考
对 pair
、tuple
定义哈希
struct hash_pair {
template <class T1, class T2>
size_t operator()(const pair<T1, T2> &p) const {
return hash<T1>()(p.fi) ^ hash<T2>()(p.se);
}
};
unordered_set<pair<int, int>, int, hash_pair> S;
unordered_map<tuple<int, int, int>, int, hash_pair> M;
对结构体定义哈希
需要两个条件,一个是在结构体中重载等于号(区别于非哈希容器需要重载小于号,如上所述,当冲突时编译器需要根据重载的等于号判断),第二是写一个哈希函数。注意 hash<>()
的尖括号中的类型匹配。
struct fff {
string x, y;
int z;
friend bool operator == (const fff &a, const fff &b) {
return a.x == b.x || a.y == b.y || a.z == b.z;
}
};
struct hash_fff {
size_t operator()(const fff &p) const {
return hash<string>()(p.x) ^ hash<string>()(p.y) ^ hash<int>()(p.z);
}
};
unordered_map<fff, int, hash_fff> mp;
对 vector
定义哈希
以下两个方法均可。注意 hash<>()
的尖括号中的类型匹配。
struct hash_vector {
size_t operator()(const vector<int> &p) const {
size_t seed = 0;
for (auto it : p) {
seed ^= hash<int>()(it);
}
return seed;
}
};
unordered_map<vector<int>, int, hash_vector> mp;
namespace std {
template<> struct hash<vector<int>> {
size_t operator()(const vector<int> &p) const {
size_t seed = 0;
for (int i : p) {
seed ^= hash<int>()(i) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
return seed;
}
};
}
unordered_set<vector<int> > S;
标签:12,hash,迭代,int,大红大紫,tt,元素,const,库函数 来源: https://www.cnblogs.com/WIDA/p/16676298.html