其他分享
首页 > 其他分享> > 【ZJSU - 大红大紫:ACM - Template】比赛用模板12:STL与库函数

【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\)

可以快捷的将一串字符串转换为指定进制的数字

使用方法


数值转换为字符串函数 \(\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); 有两种删除方式:

//连续头部删除
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); 有两种删除方式:

慎用随机访问!——当不确定某次查询是否存在于容器中时,不要直接使用下标查询,而是先使用 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 值代入,编译器就无法正常运行,这时需要我们为其手写哈希函数。而我们写的这个哈希函数的正确性其实并不是特别重要(但是不可以没有),当发生冲突时编译器会调用 keyoperator == 函数进行进一步判断。参考

pairtuple 定义哈希

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