ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【ZJSU - 大红大紫:ACM - Template】比赛用模板12:STL与库函数

2022-09-10 13:02:00  阅读:182  来源: 互联网

标签:12 hash 迭代 int 大红大紫 tt 元素 const 库函数


\(\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 值代入,编译器就无法正常运行,这时需要我们为其手写哈希函数。而我们写的这个哈希函数的正确性其实并不是特别重要(但是不可以没有),当发生冲突时编译器会调用 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有